From 2dfac93c5be5f65078961e1a5d0847f6a86e9f76 Mon Sep 17 00:00:00 2001 From: Edward McFarlane <3036610+emcfarlane@users.noreply.github.com> Date: Tue, 28 May 2024 13:58:41 -0400 Subject: [PATCH] Support annotated tags for push git metadata (#3013) Fix resolving of annotated tags on push with `--git-metadata` flag by fetching from the remote and then using `--points-at` to discover each ref and tag. Currently only lightweight tags are resolved as the commit sha for annotated tags doesn't directly point to the commit. --- private/pkg/git/git.go | 107 +++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/private/pkg/git/git.go b/private/pkg/git/git.go index 4ec5b72fd7..7b0b59b9eb 100644 --- a/private/pkg/git/git.go +++ b/private/pkg/git/git.go @@ -21,7 +21,9 @@ import ( "errors" "fmt" "os/exec" + "path" "regexp" + "sort" "strings" "github.com/bufbuild/buf/private/pkg/app" @@ -33,10 +35,7 @@ import ( ) const ( - gitCommand = "git" - gitOriginRemote = "origin" - tagsPrefix = "refs/tags/" - headsPrefix = "refs/heads/" + gitCommand = "git" ) var ( @@ -233,7 +232,7 @@ func CheckDirectoryIsValidGitCheckout( ); err != nil { var exitErr *exec.ExitError if errors.As(err, &exitErr) { - if exitErr.ProcessState.ExitCode() == 128 { + if exitErr.ExitCode() == 128 { return fmt.Errorf("dir %s: %w", dir, ErrInvalidGitCheckout) } } @@ -314,13 +313,61 @@ func GetRefsForGitCommitAndRemote( dir string, remote string, gitCommitSha string, +) ([]string, error) { + if err := fetchRemoteRefs(ctx, runner, envContainer, dir, remote); err != nil { + return nil, err + } + refs, err := getRefsPointsAt(ctx, runner, envContainer, dir, remote, gitCommitSha) + if err != nil { + return nil, err + } + tags, err := getTagsPointsAt(ctx, runner, envContainer, dir, gitCommitSha) + if err != nil { + return nil, err + } + values := append(refs, tags...) + sort.Strings(values) + return values, nil +} + +func fetchRemoteRefs( + ctx context.Context, + runner command.Runner, + envContainer app.EnvContainer, + dir string, + remote string, +) error { + if err := runner.Run( + ctx, + gitCommand, + command.RunWithArgs("fetch", "--tags", remote), + command.RunWithDir(dir), + command.RunWithEnv(app.EnvironMap(envContainer)), + ); err != nil { + return err + } + return nil +} + +func getRefsPointsAt( + ctx context.Context, + runner command.Runner, + envContainer app.EnvContainer, + dir string, + remote string, + gitCommitSha string, ) ([]string, error) { stdout := bytes.NewBuffer(nil) stderr := bytes.NewBuffer(nil) if err := runner.Run( ctx, gitCommand, - command.RunWithArgs("ls-remote", "--heads", "--tags", "--refs", remote), + command.RunWithArgs( + "for-each-ref", + "--points-at", gitCommitSha, + "--format", "%(refname:short)", + path.Join("refs/remotes", remote), + ), command.RunWithStdout(stdout), command.RunWithStderr(stderr), command.RunWithDir(dir), @@ -328,22 +375,40 @@ func GetRefsForGitCommitAndRemote( ); err != nil { return nil, err } - scanner := bufio.NewScanner(stdout) - var refs []string - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if ref, found := strings.CutPrefix(line, gitCommitSha); found { - ref = strings.TrimSpace(ref) - if tag, isTag := strings.CutPrefix(ref, tagsPrefix); isTag { - refs = append(refs, tag) - continue - } - if branch, isBranchHead := strings.CutPrefix(ref, headsPrefix); isBranchHead { - refs = append(refs, branch) - } - } + lines := getAllTrimmedLinesFromBuffer(stdout) + originPrefix := fmt.Sprintf("%s/", remote) + for i, line := range lines { + lines[i] = strings.TrimPrefix(line, originPrefix) + } + return lines, nil +} + +func getTagsPointsAt( + ctx context.Context, + runner command.Runner, + envContainer app.EnvContainer, + dir string, + gitCommitSha string, +) ([]string, error) { + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + if err := runner.Run( + ctx, + gitCommand, + command.RunWithArgs( + "for-each-ref", + "--points-at", gitCommitSha, + "--format", "%(refname:short)", + "refs/tags", + ), + command.RunWithStdout(stdout), + command.RunWithStderr(stderr), + command.RunWithDir(dir), + command.RunWithEnv(app.EnvironMap(envContainer)), + ); err != nil { + return nil, err } - return refs, nil + return getAllTrimmedLinesFromBuffer(stdout), nil } func getAllTrimmedLinesFromBuffer(buffer *bytes.Buffer) []string {