Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 16 additions & 31 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,40 +192,25 @@ func TruncateString(str string, maxLength int) string {

func RunCmdAndWait(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...)

stdout, err := cmd.StdoutPipe()
if err != nil {
return "", err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return "", err
}

err = cmd.Start()
resp, err := cmd.Output()
if err != nil {
return "", err
}

resp, err := io.ReadAll(stdout)
if err != nil {
return "", err
}
errB, err := io.ReadAll(stderr)
if err != nil {
//nolint
return "", nil
}

err = cmd.Wait()
if err != nil {
// in case of error, capture the exact message.
if len(errB) > 0 {
return "", errors.New(string(errB))
cmdStr := name + " " + strings.Join(args, " ")
output := ""
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
output = strings.TrimSpace(string(exitErr.Stderr))
}
return "", err
if output == "" {
output = strings.TrimSpace(string(resp))
}
if output != "" {
if exitErr != nil {
return "", fmt.Errorf("error running \"%s\" (exit code %d): %s", cmdStr, exitErr.ExitCode(), output)
}
return "", fmt.Errorf("error running \"%s\": %s", cmdStr, output)
}
return "", fmt.Errorf("error running \"%s\": %w", cmdStr, err)
}

return string(resp), nil
}

Expand Down
47 changes: 47 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,53 @@ func cleanupTempDir(t *testing.T, fileName string) {
assert.NoError(t, err)
}

func TestRunCmdAndWait(t *testing.T) {
t.Run("successful command returns stdout", func(t *testing.T) {
t.Parallel()
var out string
var err error
if runtime.GOOS == "windows" {
out, err = RunCmdAndWait("cmd", "/c", "echo", "hello")
} else {
out, err = RunCmdAndWait("echo", "hello")
}
assert.NoError(t, err)
assert.Contains(t, out, "hello")
})

t.Run("failed command with stderr includes command name and stderr in error", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping on Windows")
}
t.Parallel()
_, err := RunCmdAndWait("sh", "-c", "echo myerror >&2; exit 1")
assert.Error(t, err)
assert.Contains(t, err.Error(), `error running "sh -c echo myerror >&2; exit 1" (exit code 1)`)
assert.Contains(t, err.Error(), "myerror")
})

t.Run("failed command with no stderr falls back to stdout in error", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping on Windows")
}
t.Parallel()
_, err := RunCmdAndWait("sh", "-c", "echo myoutput; exit 1")
assert.Error(t, err)
assert.Contains(t, err.Error(), `error running "sh -c echo myoutput; exit 1" (exit code 1)`)
assert.Contains(t, err.Error(), "myoutput")
})

t.Run("failed command with no output wraps original error with command name", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping on Windows")
}
t.Parallel()
_, err := RunCmdAndWait("sh", "-c", "exit 1")
assert.Error(t, err)
assert.Contains(t, err.Error(), `error running "sh -c exit 1"`)
})
}

func TestSanitizeDir(t *testing.T) {
testcases := []struct {
name string
Expand Down