Skip to content

Commit

Permalink
Formalize gh process exit codes
Browse files Browse the repository at this point in the history
Here are the statuses:
- 0: success
- 1: misc. error
- 2: user interrupt/cancellation
- 4: authentication needed

These old exit codes are now changed to "1":
- we used to return "2" for config file errors;
- we used to return "2" for alias expansion errors;
- we used to return "3" for alias runtime errors.

I do not believe that there is a need to distinguish these specific
cases via exit status, and converting them to "1" frees codes "2" and
"3" for more practical use.
  • Loading branch information
mislav committed Mar 4, 2021
1 parent dd34cae commit 9234163
Showing 1 changed file with 34 additions and 13 deletions.
47 changes: 34 additions & 13 deletions cmd/gh/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

surveyCore "github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/build"
"github.com/cli/cli/internal/config"
Expand All @@ -32,7 +33,21 @@ import (

var updaterEnabled = ""

type exitCode int

const (
exitOK exitCode = 0
exitError exitCode = 1
exitCancel exitCode = 2
exitAuth exitCode = 4
)

func main() {
code := mainRun()
os.Exit(int(code))
}

func mainRun() exitCode {
buildDate := build.Date
buildVersion := build.Version

Expand Down Expand Up @@ -78,7 +93,7 @@ func main() {
cfg, err := cmdFactory.Config()
if err != nil {
fmt.Fprintf(stderr, "failed to read configuration: %s\n", err)
os.Exit(2)
return exitError
}

if prompt, _ := cfg.Get("", "prompt"); prompt == "disabled" {
Expand All @@ -102,7 +117,7 @@ func main() {
expandedArgs, isShell, err = expand.ExpandAlias(cfg, os.Args, nil)
if err != nil {
fmt.Fprintf(stderr, "failed to process aliases: %s\n", err)
os.Exit(2)
return exitError
}

if hasDebug {
Expand All @@ -113,7 +128,7 @@ func main() {
exe, err := safeexec.LookPath(expandedArgs[0])
if err != nil {
fmt.Fprintf(stderr, "failed to run external command: %s", err)
os.Exit(3)
return exitError
}

externalCmd := exec.Command(exe, expandedArgs[1:]...)
Expand All @@ -125,14 +140,14 @@ func main() {
err = preparedCmd.Run()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
os.Exit(ee.ExitCode())
return exitCode(ee.ExitCode())
}

fmt.Fprintf(stderr, "failed to run external command: %s", err)
os.Exit(3)
return exitError
}

os.Exit(0)
return exitOK
}
}

Expand All @@ -142,29 +157,33 @@ func main() {
fmt.Fprintln(stderr, cs.Bold("Welcome to GitHub CLI!"))
fmt.Fprintln(stderr)
fmt.Fprintln(stderr, "To authenticate, please run `gh auth login`.")
os.Exit(4)
return exitAuth
}

rootCmd.SetArgs(expandedArgs)

if cmd, err := rootCmd.ExecuteC(); err != nil {
if err == cmdutil.SilentError {
os.Exit(1)
return exitError
} else if cmdutil.IsUserCancellation(err) {
os.Exit(2)
if errors.Is(err, terminal.InterruptErr) {
// ensure the next shell prompt will start on its own line
fmt.Fprint(stderr, "\n")
}
return exitCancel
}

printError(stderr, err, cmd, hasDebug)

var httpErr api.HTTPError
if errors.As(err, &httpErr) && httpErr.StatusCode == 401 {
fmt.Println("hint: try authenticating with `gh auth login`")
fmt.Fprintln(stderr, "hint: try authenticating with `gh auth login`")
}

os.Exit(1)
return exitError
}
if root.HasFailed() {
os.Exit(1)
return exitError
}

newRelease := <-updateMessageChan
Expand All @@ -175,7 +194,7 @@ func main() {
}
if isHomebrew && isRecentRelease(newRelease.PublishedAt) {
// do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
return
return exitOK
}
fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
ansi.Color("A new release of gh is available:", "yellow"),
Expand All @@ -187,6 +206,8 @@ func main() {
fmt.Fprintf(stderr, "%s\n\n",
ansi.Color(newRelease.URL, "yellow"))
}

return exitOK
}

func printError(out io.Writer, err error, cmd *cobra.Command, debug bool) {
Expand Down

0 comments on commit 9234163

Please sign in to comment.