84 lines
2.8 KiB
Go
84 lines
2.8 KiB
Go
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 takes the token of the tea login for the same host, and its
|
||
// URL as the API root; with no such login, or one without a token, it falls back to `GITEA_TOKEN` and the origin's web root.
|
||
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
|
||
}
|
||
baseURL, token := o.WebURL, ""
|
||
if login != nil {
|
||
baseURL, token = login.URL, login.Token
|
||
}
|
||
if token == "" {
|
||
token = env.Getenv("GITEA_TOKEN")
|
||
}
|
||
if token == "" {
|
||
return nil, fmt.Errorf("没有 %s 的 Gitea token:tea config 里没有该 host 的 login 或其 token 为空,GITEA_TOKEN 也没设置;请运行 `tea login add` 或设置 GITEA_TOKEN", o.Host)
|
||
}
|
||
return NewGitea(baseURL, token, o.Owner, o.Repo), nil
|
||
}
|