Skip to content

Commit e96d974

Browse files
authored
Merge pull request cli#3023 from cli/cancel-error-status
Issue/pr create: exit with nonzero status code when "Cancel" was chosen
2 parents 1eefb6b + 9234163 commit e96d974

File tree

11 files changed

+66
-24
lines changed

11 files changed

+66
-24
lines changed

cmd/gh/main.go

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

3334
var 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+
3545
func 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,23 +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 {
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
@@ -169,7 +194,7 @@ func main() {
169194
}
170195
if isHomebrew && isRecentRelease(newRelease.PublishedAt) {
171196
// do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
172-
return
197+
return exitOK
173198
}
174199
fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
175200
ansi.Color("A new release of gh is available:", "yellow"),
@@ -181,13 +206,11 @@ func main() {
181206
fmt.Fprintf(stderr, "%s\n\n",
182207
ansi.Color(newRelease.URL, "yellow"))
183208
}
209+
210+
return exitOK
184211
}
185212

186213
func printError(out io.Writer, err error, cmd *cobra.Command, debug bool) {
187-
if err == cmdutil.SilentError {
188-
return
189-
}
190-
191214
var dnsError *net.DNSError
192215
if errors.As(err, &dnsError) {
193216
fmt.Fprintf(out, "error connecting to %s\n", dnsError.Name)

pkg/cmd/gist/edit/edit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func editRun(opts *EditOptions) error {
195195
case "Submit":
196196
stop = true
197197
case "Cancel":
198-
return cmdutil.SilentError
198+
return cmdutil.CancelError
199199
}
200200

201201
if stop {

pkg/cmd/gist/edit/edit_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func Test_editRun(t *testing.T) {
194194
as.StubOne("unix.md")
195195
as.StubOne("Cancel")
196196
},
197-
wantErr: "SilentError",
197+
wantErr: "CancelError",
198198
gist: &shared.Gist{
199199
ID: "1234",
200200
Files: map[string]*shared.GistFile{

pkg/cmd/issue/create/create.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ func createRun(opts *CreateOptions) (err error) {
260260

261261
if action == prShared.CancelAction {
262262
fmt.Fprintln(opts.IO.ErrOut, "Discarding.")
263+
err = cmdutil.CancelError
263264
return
264265
}
265266
} else {

pkg/cmd/pr/create/create.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ func createRun(opts *CreateOptions) (err error) {
311311

312312
if action == shared.CancelAction {
313313
fmt.Fprintln(opts.IO.ErrOut, "Discarding.")
314-
return nil
314+
err = cmdutil.CancelError
315+
return
315316
}
316317

317318
err = handlePush(*opts, *ctx)
@@ -553,7 +554,7 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
553554
} else if pushOptions[selectedOption] == "Skip pushing the branch" {
554555
isPushEnabled = false
555556
} else if pushOptions[selectedOption] == "Cancel" {
556-
return nil, cmdutil.SilentError
557+
return nil, cmdutil.CancelError
557558
} else {
558559
// "Create a fork of ..."
559560
if baseRepo.IsPrivate {

pkg/cmd/pr/merge/merge.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func mergeRun(opts *MergeOptions) error {
227227
}
228228
if action == shared.CancelAction {
229229
fmt.Fprintln(opts.IO.ErrOut, "Cancelled.")
230-
return cmdutil.SilentError
230+
return cmdutil.CancelError
231231
}
232232
}
233233

pkg/cmd/pr/merge/merge_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ func TestPRMerge_interactiveCancelled(t *testing.T) {
895895
as.StubOne("Cancel") // Confirm submit survey
896896

897897
output, err := runCommand(http, "blueberries", true, "")
898-
if !errors.Is(err, cmdutil.SilentError) {
898+
if !errors.Is(err, cmdutil.CancelError) {
899899
t.Fatalf("got error %v", err)
900900
}
901901

pkg/cmd/pr/shared/preserve.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77

8+
"github.com/cli/cli/pkg/cmdutil"
89
"github.com/cli/cli/pkg/iostreams"
910
)
1011

@@ -18,6 +19,11 @@ func PreserveInput(io *iostreams.IOStreams, state *IssueMetadataState, createErr
1819
return
1920
}
2021

22+
if cmdutil.IsUserCancellation(*createErr) {
23+
// these errors are user-initiated cancellations
24+
return
25+
}
26+
2127
out := io.ErrOut
2228

2329
// this extra newline guards against appending to the end of a survey line

pkg/cmd/release/create/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func createRun(opts *CreateOptions) error {
256256
case "Save as draft":
257257
opts.Draft = true
258258
case "Cancel":
259-
return cmdutil.SilentError
259+
return cmdutil.CancelError
260260
default:
261261
return fmt.Errorf("invalid action: %v", opts.SubmitAction)
262262
}

pkg/cmd/release/delete/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func deleteRun(opts *DeleteOptions) error {
7878
}
7979

8080
if !confirmed {
81-
return cmdutil.SilentError
81+
return cmdutil.CancelError
8282
}
8383
}
8484

0 commit comments

Comments
 (0)