Files

85 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package forge
import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/cruldra/superpowers-vscode/cli/internal/repo"
"github.com/cruldra/superpowers-vscode/cli/internal/tea"
)
// Env is where [Open] gets credentials from; tests swap in canned answers, real runs use [SystemEnv].
type Env struct {
// GitHubToken returns the token the GitHub CLI holds for `host`.
GitHubToken func(host string) (string, error)
// TeaLogin returns the tea login whose URL points at `host`, or `nil` when there is none.
TeaLogin func(host string) (*tea.TeaLogin, error)
// Getenv reads an environment variable.
Getenv func(key string) string
}
// SystemEnv reads credentials from this machine: `gh auth token`, the tea config file and the process environment.
func SystemEnv() Env {
return Env{
GitHubToken: ghAuthToken,
TeaLogin: func(host string) (*tea.TeaLogin, error) {
path, err := tea.DefaultConfigPath()
if err != nil {
return nil, err
}
return tea.LoginForHost(path, host)
},
Getenv: os.Getenv,
}
}
// ghAuthToken asks the GitHub CLI for the token it stores for `host`.
func ghAuthToken(host string) (string, error) {
out, err := exec.Command("gh", "auth", "token", "--hostname", host).Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && len(exitErr.Stderr) > 0 {
return "", fmt.Errorf("gh auth token --hostname %s 失败: %w: %s", host, err, strings.TrimSpace(string(exitErr.Stderr)))
}
return "", fmt.Errorf("gh auth token --hostname %s 失败: %w", host, err)
}
return string(out), nil
}
// Open connects to the repo at `o` on the forge its host names, picking credentials the way that forge expects.
//
// GitHub takes the token from `gh auth token`. Gitea needs a [tea.TeaLogin] for the same host and uses its URL as the API
// root and its token; `GITEA_TOKEN` only fills in when that login has no token, so the variable never reaches a host the
// user has not logged in to with tea.
func Open(o repo.Origin, env Env) (Forge, error) {
if KindForHost(o.Host) == KindGitHub {
token, err := env.GitHubToken(o.Host)
if err != nil {
return nil, fmt.Errorf("取 GitHub token 失败,请先运行 `gh auth login`: %w", err)
}
token = strings.TrimSpace(token)
if token == "" {
return nil, fmt.Errorf("gh 没有 %s 的 token,请先运行 `gh auth login`", o.Host)
}
return NewGitHub(GitHubAPIBaseURL, token, o.Owner, o.Repo), nil
}
login, err := env.TeaLogin(o.Host)
if err != nil {
return nil, err
}
if login == nil {
return nil, fmt.Errorf("tea config 里没有 %s 的 login,spx 不会把 Gitea token 发给 tea 没登录过的 host;请先运行 `tea login add` 登录这个 host", o.Host)
}
token := login.Token
if token == "" {
token = env.Getenv("GITEA_TOKEN")
}
if token == "" {
return nil, fmt.Errorf("tea config 里 %s 的 login %q 没有 token,GITEA_TOKEN 也没设置;请重新运行 `tea login add` 或设置 GITEA_TOKEN", o.Host, login.Name)
}
return NewGitea(login.URL, token, o.Owner, o.Repo), nil
}