sourcegraph/dev/ci/gitops/git_ops.go
Bolaji Olajide 067115910c
fix(ci): check command out for error when git fails (#63993)
Closes [#1110](https://github.com/sourcegraph/devx-support/issues/1110)
Closes DINF-96

We don't print the stdErr when a command fails … in particular when git
fails. Therefore we see very little in the panic of what went wrong.

Explanation:
> There's a weird behavior that occurs where an error isn't accessible
in the err variable
// from a *Cmd executing a git command after calling CombinedOutput().
// This occurs due to how Git handles errors and how the exec package in
Go interprets the command's output.
// Git often writes error messages to stderr, but it might still exit
with a status code of 0 (indicating success).
// In this case, CombinedOutput() won't return an error, but the error
message will be in the out variable.

## Test plan

Manual testing

```go
func main() {
	ctx := context.Background()
	cmd := exec.CommandContext(ctx, "git", "rev-parse", "--is-inside-work-tree")
	out, err := handleGitCommandExec(cmd)
	if err != nil {
		// er := errors.Wrap(err, fmt.Sprintf("idsdsd: %s", string(out)))
		panic(err)
	}
	fmt.Println("hello", string(out))
}
```

## Changelog

<!-- OPTIONAL; info at
https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c
-->
2024-07-23 09:56:33 -05:00

99 lines
2.8 KiB
Go

package gitops
import (
"context"
"fmt"
"os/exec"
"strings"
"github.com/sourcegraph/sourcegraph/internal/execute"
"github.com/sourcegraph/sourcegraph/internal/oobmigration"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
var ErrNoTags = errors.New("no tags found")
func determineDiffArgs(baseBranch, commit string) (string, error) {
// We have a different base branch (possibily) and on aspect agents we are in a detached state with only 100 commit depth
// so we might not know about this base branch ... so we first fetch the base and then diff
//
// Determine the base branch
if baseBranch == "" {
// When the base branch is not set, then this is probably a build where a commit got merged
// onto the current branch. So we just diff with the current commit
return "@^", nil
}
// fetch the branch to make sure it exists
refspec := fmt.Sprintf("+refs/heads/%s:refs/remotes/origin/%s", baseBranch, baseBranch)
if _, err := exec.Command("git", "fetch", "origin", refspec).Output(); err != nil {
return "", errors.Newf("failed to fetch %s: %s", baseBranch, err)
} else {
return fmt.Sprintf("origin/%s...%s", baseBranch, commit), nil
}
}
func GetHEADChangedFiles() ([]string, error) {
output, err := execute.Git(context.Background(), "diff", "--name-only", "@^")
if err != nil {
return nil, err
}
changedFiles := strings.Split(strings.TrimSpace(string(output)), "\n")
return changedFiles, nil
}
func GetBranchChangedFiles(baseBranch, commit string) ([]string, error) {
diffArgs, err := determineDiffArgs(baseBranch, commit)
if err != nil {
return nil, err
}
output, err := execute.Git(context.Background(), "diff", "--name-only", diffArgs)
if err != nil {
return nil, err
}
changedFiles := strings.Split(strings.TrimSpace(string(output)), "\n")
return changedFiles, nil
}
func GetLatestTag() (string, error) {
output, err := execute.Git(context.Background(), "tag", "--list", "v*")
if err != nil {
return "", err
}
tagMap := map[string]struct{}{}
for _, tag := range strings.Split(string(output), "\n") {
if version, ok := oobmigration.NewVersionFromString(tag); ok {
tagMap[version.String()] = struct{}{}
}
}
if len(tagMap) == 0 {
return "", ErrNoTags
}
versions := make([]oobmigration.Version, 0, len(tagMap))
for tag := range tagMap {
version, _ := oobmigration.NewVersionFromString(tag)
versions = append(versions, version)
}
oobmigration.SortVersions(versions)
return versions[len(versions)-1].String(), nil
}
func HasIncludedCommit(commits ...string) (bool, error) {
found := false
var errs error
for _, mustIncludeCommit := range commits {
output, err := execute.Git(context.Background(), "merge-base", "--is-ancestor", mustIncludeCommit, "HEAD")
if err == nil {
found = true
break
}
errs = errors.Append(errs, errors.Errorf("%v | Output: %q", err, string(output)))
}
return found, errs
}