67 lines
1.9 KiB
Go
67 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type commandRunner func(context.Context, string, ...string) ([]byte, error)
|
|
|
|
func resolveToken(endpoint string) (string, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
return resolveTokenWith(ctx, endpoint, os.Getenv, runCommand)
|
|
}
|
|
|
|
func resolveTokenWith(ctx context.Context, endpoint string, getenv func(string) string, run commandRunner) (string, error) {
|
|
if token := firstNonEmpty(
|
|
getenv("GH_TOKEN"),
|
|
getenv("GITHUB_TOKEN"),
|
|
getenv("GH_ENTERPRISE_TOKEN"),
|
|
getenv("GITHUB_ENTERPRISE_TOKEN"),
|
|
); token != "" {
|
|
return token, nil
|
|
}
|
|
|
|
host, err := githubHost(endpoint)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
output, err := run(ctx, "gh", "auth", "token", "--hostname", host)
|
|
if err != nil {
|
|
if errors.Is(err, exec.ErrNotFound) {
|
|
return "", errors.New("no environment token found and gh is not installed; install gh and run `gh auth login`, or set GH_TOKEN")
|
|
}
|
|
if ctx.Err() != nil {
|
|
return "", fmt.Errorf("read authentication from gh: %w", ctx.Err())
|
|
}
|
|
return "", fmt.Errorf("no usable authentication for %s; run `gh auth login --hostname %s`, or set GH_TOKEN: %w", host, host, err)
|
|
}
|
|
token := strings.TrimSpace(string(output))
|
|
if token == "" {
|
|
return "", fmt.Errorf("gh returned an empty token for %s; run `gh auth login --hostname %s`", host, host)
|
|
}
|
|
return token, nil
|
|
}
|
|
|
|
func githubHost(endpoint string) (string, error) {
|
|
parsed, err := url.Parse(endpoint)
|
|
if err != nil || parsed.Scheme == "" || parsed.Hostname() == "" {
|
|
return "", fmt.Errorf("invalid GitHub GraphQL endpoint %q", endpoint)
|
|
}
|
|
if parsed.Hostname() == "api.github.com" {
|
|
return "github.com", nil
|
|
}
|
|
return parsed.Hostname(), nil
|
|
}
|
|
|
|
func runCommand(ctx context.Context, name string, args ...string) ([]byte, error) {
|
|
return exec.CommandContext(ctx, name, args...).Output()
|
|
}
|