@@ -13,6 +13,7 @@ import (
1313 "time"
1414
1515 surveyCore "github.com/AlecAivazis/survey/v2/core"
16+ "github.com/AlecAivazis/survey/v2/terminal"
1617 "github.com/cli/cli/api"
1718 "github.com/cli/cli/internal/build"
1819 "github.com/cli/cli/internal/config"
@@ -32,7 +33,21 @@ import (
3233
3334var updaterEnabled = ""
3435
36+ type exitCode int
37+
38+ const (
39+ exitOK exitCode = 0
40+ exitError exitCode = 1
41+ exitCancel exitCode = 2
42+ exitAuth exitCode = 4
43+ )
44+
3545func main () {
46+ code := mainRun ()
47+ os .Exit (int (code ))
48+ }
49+
50+ func mainRun () exitCode {
3651 buildDate := build .Date
3752 buildVersion := build .Version
3853
@@ -78,7 +93,7 @@ func main() {
7893 cfg , err := cmdFactory .Config ()
7994 if err != nil {
8095 fmt .Fprintf (stderr , "failed to read configuration: %s\n " , err )
81- os . Exit ( 2 )
96+ return exitError
8297 }
8398
8499 if prompt , _ := cfg .Get ("" , "prompt" ); prompt == "disabled" {
@@ -102,7 +117,7 @@ func main() {
102117 expandedArgs , isShell , err = expand .ExpandAlias (cfg , os .Args , nil )
103118 if err != nil {
104119 fmt .Fprintf (stderr , "failed to process aliases: %s\n " , err )
105- os . Exit ( 2 )
120+ return exitError
106121 }
107122
108123 if hasDebug {
@@ -113,7 +128,7 @@ func main() {
113128 exe , err := safeexec .LookPath (expandedArgs [0 ])
114129 if err != nil {
115130 fmt .Fprintf (stderr , "failed to run external command: %s" , err )
116- os . Exit ( 3 )
131+ return exitError
117132 }
118133
119134 externalCmd := exec .Command (exe , expandedArgs [1 :]... )
@@ -125,14 +140,14 @@ func main() {
125140 err = preparedCmd .Run ()
126141 if err != nil {
127142 if ee , ok := err .(* exec.ExitError ); ok {
128- os . Exit (ee .ExitCode ())
143+ return exitCode (ee .ExitCode ())
129144 }
130145
131146 fmt .Fprintf (stderr , "failed to run external command: %s" , err )
132- os . Exit ( 3 )
147+ return exitError
133148 }
134149
135- os . Exit ( 0 )
150+ return exitOK
136151 }
137152 }
138153
@@ -142,29 +157,33 @@ func main() {
142157 fmt .Fprintln (stderr , cs .Bold ("Welcome to GitHub CLI!" ))
143158 fmt .Fprintln (stderr )
144159 fmt .Fprintln (stderr , "To authenticate, please run `gh auth login`." )
145- os . Exit ( 4 )
160+ return exitAuth
146161 }
147162
148163 rootCmd .SetArgs (expandedArgs )
149164
150165 if cmd , err := rootCmd .ExecuteC (); err != nil {
151166 if err == cmdutil .SilentError {
152- os . Exit ( 1 )
167+ return exitError
153168 } else if cmdutil .IsUserCancellation (err ) {
154- os .Exit (2 )
169+ if errors .Is (err , terminal .InterruptErr ) {
170+ // ensure the next shell prompt will start on its own line
171+ fmt .Fprint (stderr , "\n " )
172+ }
173+ return exitCancel
155174 }
156175
157176 printError (stderr , err , cmd , hasDebug )
158177
159178 var httpErr api.HTTPError
160179 if errors .As (err , & httpErr ) && httpErr .StatusCode == 401 {
161- fmt .Println ( "hint: try authenticating with `gh auth login`" )
180+ fmt .Fprintln ( stderr , "hint: try authenticating with `gh auth login`" )
162181 }
163182
164- os . Exit ( 1 )
183+ return exitError
165184 }
166185 if root .HasFailed () {
167- os . Exit ( 1 )
186+ return exitError
168187 }
169188
170189 newRelease := <- updateMessageChan
@@ -175,7 +194,7 @@ func main() {
175194 }
176195 if isHomebrew && isRecentRelease (newRelease .PublishedAt ) {
177196 // do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
178- return
197+ return exitOK
179198 }
180199 fmt .Fprintf (stderr , "\n \n %s %s → %s\n " ,
181200 ansi .Color ("A new release of gh is available:" , "yellow" ),
@@ -187,6 +206,8 @@ func main() {
187206 fmt .Fprintf (stderr , "%s\n \n " ,
188207 ansi .Color (newRelease .URL , "yellow" ))
189208 }
209+
210+ return exitOK
190211}
191212
192213func printError (out io.Writer , err error , cmd * cobra.Command , debug bool ) {
0 commit comments