From a71db0945aa0292f57b556ea5a432c33f25c2538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 3 Aug 2023 11:48:18 +0100 Subject: [PATCH] cmd/cueckoo: start using `git credential` for github auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want this for Gerrit, but for consistency and to test it out first, do it with GitHub first. Note that we still support the env vars as a fallback for now, in case anyone struggles with the git credential helper setup. However, we would recommend to avoid the env vars after this change, as many git credential helpers like libsecret are safer than env vars. Once again, verified that runtrybot still works with a dummy CL. Signed-off-by: Daniel Martí Change-Id: Idc188d2168c168c36afd080bed6349e2d042b463 Dispatch-Trailer: {"type":"trybot","CL":557234,"patchset":4,"ref":"refs/changes/34/557234/4","targetBranch":"master"} --- cmd/cueckoo/cmd/util.go | 79 +++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/cmd/cueckoo/cmd/util.go b/cmd/cueckoo/cmd/util.go index 6923454..a185cb8 100644 --- a/cmd/cueckoo/cmd/util.go +++ b/cmd/cueckoo/cmd/util.go @@ -19,7 +19,9 @@ import ( "encoding/json" "fmt" "io" + "net/url" "os" + "os/exec" "strings" "github.com/andygrunwald/go-gerrit" @@ -119,16 +121,18 @@ func loadConfig(ctx context.Context) (*config, error) { } } - var auth github.BasicAuthTransport - auth.Username, err = mustGetEnv("GITHUB_USER") - if err != nil { - return nil, err - } - auth.Password, err = mustGetEnv("GITHUB_PAT") - if err != nil { - return nil, err + githubUser, githubPassword, err := gitCredentials(ctx, githubURL) + if githubUser == "" || githubPassword == "" || err != nil { + // Fall back to the manual env vars. + githubUser = os.Getenv("GITHUB_USER") + githubPassword = os.Getenv("GITHUB_PAT") + if githubUser == "" || githubPassword == "" { + return nil, fmt.Errorf("configure a git credential helper or set GITHUB_USER and GITHUB_PAT") + } } - res.githubClient = github.NewClient(auth.Client()) + githubAuth := github.BasicAuthTransport{Username: githubUser, Password: githubPassword} + res.githubClient = github.NewClient(githubAuth.Client()) + res.gerritClient, err = gerrit.NewClient(res.gerritURL, nil) if err != nil { return nil, err @@ -137,12 +141,59 @@ func loadConfig(ctx context.Context) (*config, error) { return &res, nil } -func mustGetEnv(name string) (string, error) { - val := os.Getenv(name) - if val == "" { - return "", fmt.Errorf("%s is required", name) +func gitCredentials(ctx context.Context, repoURL string) (username, password string, _ error) { + // For example: + // + // $ git credential fill + // protocol=https + // host=example.com + // path=foo.git + // ^D + // protocol=https + // host=example.com + // username=bob + // password=secr3t + + u, err := url.Parse(repoURL) + if err != nil { + return "", "", err + } + input := strings.Join([]string{ + "protocol=" + u.Scheme, + "host=" + u.Host, + "path=" + u.Path, + }, "\n") + "\n" // `git credential` wants a trailing newline + cmd := exec.CommandContext(ctx, "git", "credential", "fill") + cmd.Stdin = strings.NewReader(input) + outputBytes, err := cmd.Output() + if err != nil { + if err, _ := err.(*exec.ExitError); err != nil { + // stderr was captured by Output + return "", "", fmt.Errorf("failed to run %q: %v:\n%s", cmd.Args, err, err.Stderr) + } + return "", "", fmt.Errorf("failed to run %q: %v", cmd.Args, err) + } + for _, line := range strings.Split(string(outputBytes), "\n") { + if line == "" { + continue // ignore the trailing empty line + } + key, val, ok := strings.Cut(line, "=") + if !ok { + return "", "", fmt.Errorf("invalid output line: %q", line) + } + switch key { + case "protocol", "host", "path": + // input keys are repeated; ignore them. + case "username": + username = val + case "password": + password = val + default: + // Could happen if the user configured an auth mechanism we don't support, like oauth. + return "", "", fmt.Errorf("unknown output line key: %q", line) + } } - return val, nil + return username, password, nil } func (c *config) triggerRepositoryDispatch(owner, repo string, payload github.DispatchRequestOptions) error {