Skip to content
Merged
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
21 changes: 18 additions & 3 deletions pkg/cmd/copilot/copilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,9 @@ func runCopilot(opts *CopilotOptions) error {
return nil
}

copilotPath := findCopilotBinary()
if copilotPath == "" {
copilotPath := findCopilotBinaryFunc()
foundInPath := copilotPath != ""
if !foundInPath {
if opts.IO.CanPrompt() {
confirmed, err := opts.Prompter.Confirm("GitHub Copilot CLI is not installed. Would you like to install it?", true)
if err != nil {
Expand Down Expand Up @@ -175,12 +176,18 @@ func runCopilot(opts *CopilotOptions) error {
externalCmd.Stderr = opts.IO.ErrOut
externalCmd.Env = append(os.Environ(), "COPILOT_GH=true")

if err := externalCmd.Run(); err != nil {
if err := runExternalCmdFunc(externalCmd); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// We terminate with os.Exit here, preserving the exit code from Copilot CLI,
// and also preventing stdio writes by callers up the stack.
os.Exit(exitErr.ExitCode())
}
if foundInPath {
// We found a `copilot` binary but exec failed, possibly due to
// unusual characters in the path (see https://github.com/cli/cli/issues/13106).
// Suggest running copilot directly as a workaround.
return fmt.Errorf("%w\nFailed to run '%s', try running `copilot` directly without `gh`.", err, copilotPath)
}
return err
}
return nil
Expand All @@ -200,6 +207,14 @@ func copilotBinaryPath() string {
return filepath.Join(copilotInstallDir(), binaryName)
}

var runExternalCmdFunc = runExternalCmd

func runExternalCmd(cmd *exec.Cmd) error {
return cmd.Run()
}

var findCopilotBinaryFunc = findCopilotBinary

// findCopilotBinary returns the path to the Copilot CLI binary, if installed,
// with the following order of precedence:
// 1. `copilot` in the PATH
Expand Down
27 changes: 27 additions & 0 deletions pkg/cmd/copilot/copilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
Expand Down Expand Up @@ -589,6 +590,32 @@ func TestDownloadCopilot(t *testing.T) {
})
}

func TestRunCopilot_execFailureHint(t *testing.T) {
ios, _, _, _ := iostreams.Test()
opts := &CopilotOptions{
IO: ios,
CopilotArgs: []string{},
}

origFind := findCopilotBinaryFunc
findCopilotBinaryFunc = func() string {
return "/usr/bin/copilot"
}
t.Cleanup(func() { findCopilotBinaryFunc = origFind })

execErr := fmt.Errorf("exec failed: something went wrong")
origRun := runExternalCmdFunc
runExternalCmdFunc = func(_ *exec.Cmd) error {
return execErr
}
t.Cleanup(func() { runExternalCmdFunc = origRun })

err := runCopilot(opts)
require.Error(t, err)
require.ErrorIs(t, err, execErr)
require.Contains(t, err.Error(), "try running `copilot` directly without `gh`.")
}

func TestCopilotCommandIsSampledAt100(t *testing.T) {
spy := &telemetry.CommandRecorderSpy{}
factory := &cmdutil.Factory{}
Expand Down
Loading