feat: Allow Kong to exit with semantic exit codes (#507)
* feat: Allow Kong to exit with semantic exit codes At Block, we've instrumented a number of commandline tools and set SLOs on some tools' reliability. To do that effectively, we had to partition usage errors from reliability issues. We looked at [prior art](https://github.com/square/exit?tab=readme-ov-file#reserved-codes-and-prior-art) and, taking inspiration from HTTP, defined [a set of semantic exit codes](https://github.com/square/exit?tab=readme-ov-file#about) in ranges: 80-99 for user errors, 100-119 for system errors. We've been wrapping errors in `exit.Error` at whatever level of the stack can tell which class an error is and unwrapping them at exit (`os.Exit(exit.FromError(err))`). This adds support for semantic exit codes to Kong, to `FatalIfErrorf`, which is used internally by `kong.Parse` and often used in Kong applications. * feat: Exit 80 (Usage Error) when usage is syntactically or semantically invalid * refactor: Always exit 80 (Usage Error) on a `ParseError` but don't wrap errors from hooks in `ParseError`
This commit is contained in:
+9
-3
@@ -786,10 +786,11 @@ func TestUsageOnError(t *testing.T) {
|
||||
Flag string `help:"A required flag." required`
|
||||
}
|
||||
w := &strings.Builder{}
|
||||
exitCode := -1
|
||||
p := mustNew(t, &cli,
|
||||
kong.Writers(w, w),
|
||||
kong.Description("Some description."),
|
||||
kong.Exit(func(int) {}),
|
||||
kong.Exit(func(code int) { exitCode = code }),
|
||||
kong.UsageOnError(),
|
||||
)
|
||||
_, err := p.Parse([]string{})
|
||||
@@ -806,6 +807,7 @@ Flags:
|
||||
test: error: missing flags: --flag=STRING
|
||||
`
|
||||
assert.Equal(t, expected, w.String())
|
||||
assert.Equal(t, 80, exitCode)
|
||||
}
|
||||
|
||||
func TestShortUsageOnError(t *testing.T) {
|
||||
@@ -813,10 +815,11 @@ func TestShortUsageOnError(t *testing.T) {
|
||||
Flag string `help:"A required flag." required`
|
||||
}
|
||||
w := &strings.Builder{}
|
||||
exitCode := -1
|
||||
p := mustNew(t, &cli,
|
||||
kong.Writers(w, w),
|
||||
kong.Description("Some description."),
|
||||
kong.Exit(func(int) {}),
|
||||
kong.Exit(func(code int) { exitCode = code }),
|
||||
kong.ShortUsageOnError(),
|
||||
)
|
||||
_, err := p.Parse([]string{})
|
||||
@@ -829,6 +832,7 @@ Run "test --help" for more information.
|
||||
test: error: missing flags: --flag=STRING
|
||||
`
|
||||
assert.Equal(t, expected, w.String())
|
||||
assert.Equal(t, 80, exitCode)
|
||||
}
|
||||
|
||||
func TestCustomShortUsageOnError(t *testing.T) {
|
||||
@@ -840,10 +844,11 @@ func TestCustomShortUsageOnError(t *testing.T) {
|
||||
fmt.Fprintln(ctx.Stdout, "🤷 wish I could help")
|
||||
return nil
|
||||
}
|
||||
exitCode := -1
|
||||
p := mustNew(t, &cli,
|
||||
kong.Writers(w, w),
|
||||
kong.Description("Some description."),
|
||||
kong.Exit(func(int) {}),
|
||||
kong.Exit(func(code int) { exitCode = code }),
|
||||
kong.ShortHelp(shortHelp),
|
||||
kong.ShortUsageOnError(),
|
||||
)
|
||||
@@ -856,4 +861,5 @@ func TestCustomShortUsageOnError(t *testing.T) {
|
||||
test: error: missing flags: --flag=STRING
|
||||
`
|
||||
assert.Equal(t, expected, w.String())
|
||||
assert.Equal(t, 80, exitCode)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user