@@ -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,34 +157,41 @@ 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 {
166+ if err == cmdutil .SilentError {
167+ return exitError
168+ } else if cmdutil .IsUserCancellation (err ) {
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
174+ }
175+
151176 printError (stderr , err , cmd , hasDebug )
152177
153178 var httpErr api.HTTPError
154179 if errors .As (err , & httpErr ) && httpErr .StatusCode == 401 {
155- fmt .Println ( "hint: try authenticating with `gh auth login`" )
180+ fmt .Fprintln ( stderr , "hint: try authenticating with `gh auth login`" )
156181 }
157182
158- os . Exit ( 1 )
183+ return exitError
159184 }
160185 if root .HasFailed () {
161- os . Exit ( 1 )
186+ return exitError
162187 }
163188
164189 newRelease := <- updateMessageChan
165190 if newRelease != nil {
166- isHomebrew := false
167- if ghExe , err := os .Executable (); err == nil {
168- isHomebrew = isUnderHomebrew (ghExe )
169- }
191+ isHomebrew := isUnderHomebrew (cmdFactory .Executable )
170192 if isHomebrew && isRecentRelease (newRelease .PublishedAt ) {
171193 // do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
172- return
194+ return exitOK
173195 }
174196 fmt .Fprintf (stderr , "\n \n %s %s → %s\n " ,
175197 ansi .Color ("A new release of gh is available:" , "yellow" ),
@@ -181,13 +203,11 @@ func main() {
181203 fmt .Fprintf (stderr , "%s\n \n " ,
182204 ansi .Color (newRelease .URL , "yellow" ))
183205 }
206+
207+ return exitOK
184208}
185209
186210func printError (out io.Writer , err error , cmd * cobra.Command , debug bool ) {
187- if err == cmdutil .SilentError {
188- return
189- }
190-
191211 var dnsError * net.DNSError
192212 if errors .As (err , & dnsError ) {
193213 fmt .Fprintf (out , "error connecting to %s\n " , dnsError .Name )
0 commit comments