mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 15:12:02 +00:00
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 -->
84 lines
2.0 KiB
Go
84 lines
2.0 KiB
Go
package execute
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/sourcegraph/sourcegraph/lib/errors"
|
|
)
|
|
|
|
type cmdErr struct {
|
|
err error
|
|
exitCode int
|
|
}
|
|
|
|
func (g *cmdErr) Error() string {
|
|
return g.err.Error()
|
|
}
|
|
|
|
func (g *cmdErr) ExitCode() int {
|
|
return g.exitCode
|
|
}
|
|
|
|
func (g *cmdErr) Unwrap() error {
|
|
return g.err
|
|
}
|
|
|
|
// HandleGitCommandExec 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.
|
|
func handleGitCommandExec(cmd *exec.Cmd) ([]byte, error) {
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
maybeErrMessage := strings.Trim(stderr.String(), "\n")
|
|
if strings.HasPrefix(maybeErrMessage, "fatal:") || strings.HasPrefix(maybeErrMessage, "error:") {
|
|
exitCode := 1
|
|
var exitErr *exec.ExitError
|
|
if errors.As(err, &exitErr) {
|
|
exitCode = exitErr.ExitCode()
|
|
}
|
|
return nil, &cmdErr{
|
|
err: errors.New(maybeErrMessage),
|
|
exitCode: exitCode,
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return stdout.Bytes(), nil
|
|
}
|
|
|
|
func Git(ctx context.Context, args ...string) ([]byte, error) {
|
|
return handleGitCommandExec(GitCmd(ctx, args...))
|
|
}
|
|
|
|
func GitCmd(ctx context.Context, args ...string) *exec.Cmd {
|
|
return exec.CommandContext(ctx, "git", args...)
|
|
}
|
|
|
|
func GHCmd(ctx context.Context, args ...string) *exec.Cmd {
|
|
return exec.CommandContext(ctx, "gh", args...)
|
|
}
|
|
|
|
func GH(ctx context.Context, args ...string) ([]byte, error) {
|
|
cmd := GHCmd(ctx, args...)
|
|
|
|
var stdout bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = os.Stdin
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return nil, err
|
|
}
|
|
return stdout.Bytes(), nil
|
|
}
|