feat(bonfire): global flags, Artisan list and exit codes
- Global options -v/-vv/-vvv, -q, --ansi/--no-ansi, -n and --env on every command; PeekGlobals reads them before config loads, OnGlobals hooks re-apply them after parsing (diary.ApplyGlobals in the generated main) - Bare binary, --help and list print the Artisan list with the version header from BuildVersion and namespace blocks - Execute runs the root through fang (no version, man page or completions) and returns 0, 1 or 2; Fail renders errors before the root exists - NewRoot rejects duplicate command names (ErrDuplicateCommand) and flags that reuse a global option (ErrReservedFlag) - Generated main and summer use PeekGlobals, compass.Open with --env, diary.FromGlobals and bonfire.Execute; docs checker knows the globals
This commit is contained in:
101
modules/bonfire/execute.go
Normal file
101
modules/bonfire/execute.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package bonfire
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"charm.land/fang/v2"
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Exit codes returned by Execute and Fail.
|
||||
const (
|
||||
// ExitOK means the command succeeded.
|
||||
ExitOK = 0
|
||||
// ExitFailure means the command failed.
|
||||
ExitFailure = 1
|
||||
// ExitUsage means the command was called wrongly: an unknown command or
|
||||
// flag, or a wrong number of arguments.
|
||||
ExitUsage = 2
|
||||
)
|
||||
|
||||
// Execute runs root with args and returns the process exit code: ExitOK,
|
||||
// ExitFailure, or ExitUsage for usage errors. With no command word in args
|
||||
// (only global options, -h or --help) it prints the command list. Errors are
|
||||
// rendered on the root's error stream by RenderError through fang's error
|
||||
// handler; fang is used without its version flag, man page and completion
|
||||
// commands.
|
||||
func Execute(ctx context.Context, root *cobra.Command, args []string) int {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
if args == nil {
|
||||
// cobra reads os.Args when the args are nil.
|
||||
args = []string{}
|
||||
}
|
||||
g := PeekGlobals(args)
|
||||
if !hasCommandWord(args) {
|
||||
args = slices.DeleteFunc(slices.Clone(args), func(a string) bool {
|
||||
return a == "-h" || a == "--help"
|
||||
})
|
||||
}
|
||||
resetFlags(root)
|
||||
root.SetArgs(args)
|
||||
name := root.Name()
|
||||
err := fang.Execute(ctx, root,
|
||||
fang.WithoutVersion(),
|
||||
fang.WithoutManpage(),
|
||||
fang.WithoutCompletions(),
|
||||
fang.WithErrorHandler(func(w io.Writer, _ fang.Styles, err error) {
|
||||
if cw, ok := w.(*colorprofile.Writer); ok {
|
||||
// RenderError applies its own color profile.
|
||||
w = cw.Forward
|
||||
}
|
||||
RenderError(w, name, err, g)
|
||||
}),
|
||||
)
|
||||
return exitCode(err)
|
||||
}
|
||||
|
||||
// Fail renders err like Execute does and returns ExitFailure (ExitOK for a
|
||||
// nil error). The generated main uses it for errors that happen before the
|
||||
// command root exists, such as a configuration that does not load.
|
||||
func Fail(w io.Writer, name string, err error, g Globals) int {
|
||||
if err == nil {
|
||||
return ExitOK
|
||||
}
|
||||
RenderError(w, name, err, g)
|
||||
return ExitFailure
|
||||
}
|
||||
|
||||
func exitCode(err error) int {
|
||||
switch {
|
||||
case err == nil:
|
||||
return ExitOK
|
||||
case isUsage(err):
|
||||
return ExitUsage
|
||||
default:
|
||||
return ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
// hasCommandWord reports whether args, before "--", hold a token that is
|
||||
// neither a flag nor the value of --env.
|
||||
func hasCommandWord(args []string) bool {
|
||||
for i := 0; i < len(args); i++ {
|
||||
a := args[i]
|
||||
switch {
|
||||
case a == "--":
|
||||
return false
|
||||
case a == "--"+flagEnv:
|
||||
i++
|
||||
case strings.HasPrefix(a, "-"):
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user