permit exception handling and help fuckery

This commit is contained in:
Roberto Hidalgo 2023-01-17 22:20:46 -06:00
parent 087a978a8c
commit 424ae202cd
9 changed files with 25 additions and 75 deletions

View File

@ -27,9 +27,7 @@ func newCobraRoot(root *command.Command) *cobra.Command {
SilenceErrors: true, SilenceErrors: true,
ValidArgs: []string{""}, ValidArgs: []string{""},
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
err := cobra.OnlyValidArgs(cmd, args) if err := cobra.OnlyValidArgs(cmd, args); err != nil {
if err != nil {
suggestions := []string{} suggestions := []string{}
bold := color.New(color.Bold) bold := color.New(color.Bold)
for _, l := range cmd.SuggestionsFor(args[len(args)-1]) { for _, l := range cmd.SuggestionsFor(args[len(args)-1]) {

View File

@ -20,6 +20,8 @@ import (
// ContextKeyRuntimeIndex is the string key used to store context in a cobra Command. // ContextKeyRuntimeIndex is the string key used to store context in a cobra Command.
const ContextKeyRuntimeIndex = "x-chinampa-runtime-index" const ContextKeyRuntimeIndex = "x-chinampa-runtime-index"
var ErrorHandler = errors.HandleCobraExit
var log = logrus.WithField("chinampa", "registry") var log = logrus.WithField("chinampa", "registry")
var registry = &CommandRegistry{ var registry = &CommandRegistry{
@ -66,7 +68,6 @@ func Execute(version string) error {
ccRoot.Annotations["version"] = version ccRoot.Annotations["version"] = version
ccRoot.CompletionOptions.HiddenDefaultCmd = true ccRoot.CompletionOptions.HiddenDefaultCmd = true
ccRoot.PersistentFlags().AddFlagSet(cmdRoot.FlagSet()) ccRoot.PersistentFlags().AddFlagSet(cmdRoot.FlagSet())
ccRoot.SetHelpCommand(commands.Help)
ccRoot.AddCommand(commands.Version) ccRoot.AddCommand(commands.Version)
ccRoot.AddCommand(commands.GenerateCompletions) ccRoot.AddCommand(commands.GenerateCompletions)
@ -91,6 +92,11 @@ func Execute(version string) error {
query := []string{cp} query := []string{cp}
found := false found := false
if len(query) == 1 && query[0] == "help" {
container = commands.Help
continue
}
for _, sub := range container.Commands() { for _, sub := range container.Commands() {
if sub.Name() == cp { if sub.Name() == cp {
container = sub container = sub
@ -132,6 +138,9 @@ func Execute(version string) error {
ValidArgs: []string{""}, ValidArgs: []string{""},
RunE: func(cc *cobra.Command, args []string) error { RunE: func(cc *cobra.Command, args []string) error {
if len(args) == 0 { if len(args) == 0 {
if cc.Name() == "help" {
return cc.Help()
}
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}} return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
} }
os.Exit(statuscode.NotFound) os.Exit(statuscode.NotFound)
@ -157,6 +166,7 @@ func Execute(version string) error {
cmd.Path = append(cmdRoot.Path, cmd.Path...) cmd.Path = append(cmdRoot.Path, cmd.Path...)
} }
cmdRoot.SetCobra(ccRoot) cmdRoot.SetCobra(ccRoot)
ccRoot.SetHelpCommand(commands.Help)
current, remaining, err := ccRoot.Find(os.Args[1:]) current, remaining, err := ccRoot.Find(os.Args[1:])
if err != nil { if err != nil {
@ -169,11 +179,6 @@ func Execute(version string) error {
current = sub current = sub
} }
log.Debugf("exec: calling %s", current.CommandPath()) log.Debugf("exec: calling %s", current.CommandPath())
err = current.Execute()
if err != nil {
log.Debugf("exec: error calling %s, %s", current.CommandPath(), err)
errors.HandleCobraExit(current, err)
}
return err return ErrorHandler(current, current.Execute())
} }

View File

@ -6,6 +6,7 @@ import (
"git.rob.mx/nidito/chinampa/internal/registry" "git.rob.mx/nidito/chinampa/internal/registry"
"git.rob.mx/nidito/chinampa/pkg/command" "git.rob.mx/nidito/chinampa/pkg/command"
"git.rob.mx/nidito/chinampa/pkg/runtime" "git.rob.mx/nidito/chinampa/pkg/runtime"
"github.com/spf13/cobra"
) )
func Register(cmds ...*command.Command) { func Register(cmds ...*command.Command) {
@ -21,6 +22,10 @@ type Config struct {
Description string Description string
} }
func SetErrorHandler(handlerFunc func(cmd *cobra.Command, err error) error) {
registry.ErrorHandler = handlerFunc
}
func Execute(config Config) error { func Execute(config Config) error {
runtime.Executable = config.Name runtime.Executable = config.Name
command.Root.Summary = config.Summary command.Root.Summary = config.Summary

View File

@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.rob.mx/nidito/chinampa/pkg/errors"
"git.rob.mx/nidito/chinampa/pkg/runtime" "git.rob.mx/nidito/chinampa/pkg/runtime"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -145,11 +144,7 @@ func (cmd *Command) Run(cc *cobra.Command, args []string) error {
return err return err
} }
err := cmd.Action(cmd) return cmd.Action(cmd)
if err != nil {
errors.HandleCobraExit(cmd.Cobra, err)
}
return err
} }
func (cmd *Command) SetCobra(cc *cobra.Command) { func (cmd *Command) SetCobra(cc *cobra.Command) {

View File

@ -122,7 +122,6 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
err = ctx.Err() err = ctx.Err()
return return
} }
case vs.Command != nil: case vs.Command != nil:
if vs.command == nil { if vs.command == nil {
return nil, cobra.ShellCompDirectiveError, fmt.Errorf("bug: command is nil") return nil, cobra.ShellCompDirectiveError, fmt.Errorf("bug: command is nil")
@ -168,7 +167,7 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
return nil, flag, err return nil, flag, err
} }
default: default:
return nil, flag, fmt.Errorf("Empty value source") return nil, flag, fmt.Errorf("empty value source")
} }
vs.computed = &values vs.computed = &values
@ -242,7 +241,6 @@ func (vs *ValueSource) UnmarshalYAML(node *yaml.Node) error {
} }
if t, ok := intermediate["timeout"]; ok { if t, ok := intermediate["timeout"]; ok {
if err := t.Decode(&vs.Timeout); err != nil { if err := t.Decode(&vs.Timeout); err != nil {
logrus.Errorf("could not decode timeout: %s", err) logrus.Errorf("could not decode timeout: %s", err)
return err return err

1
pkg/env/env.go vendored
View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package env package env
// Environment Variables.
var HelpUnstyled = "HELP_STYLE_PLAIN" var HelpUnstyled = "HELP_STYLE_PLAIN"
var HelpStyle = "HELP_STYLE" var HelpStyle = "HELP_STYLE"
var Verbose = "VERBOSE" var Verbose = "VERBOSE"

View File

@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package errors package errors
import "fmt"
type NotFound struct { type NotFound struct {
Msg string Msg string
Group []string Group []string
@ -13,24 +11,6 @@ type BadArguments struct {
Msg string Msg string
} }
type NotExecutable struct {
Msg string
}
type ConfigError struct {
Err error
Config string
}
type EnvironmentError struct {
Err error
}
type SubCommandExit struct {
Err error
ExitCode int
}
func (err NotFound) Error() string { func (err NotFound) Error() string {
return err.Msg return err.Msg
} }
@ -38,23 +18,3 @@ func (err NotFound) Error() string {
func (err BadArguments) Error() string { func (err BadArguments) Error() string {
return err.Msg return err.Msg
} }
func (err NotExecutable) Error() string {
return err.Msg
}
func (err SubCommandExit) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return ""
}
func (err ConfigError) Error() string {
return fmt.Sprintf("Invalid configuration %s: %v", err.Config, err.Err)
}
func (err EnvironmentError) Error() string {
return fmt.Sprintf("Invalid MILPA_ environment: %v", err.Err)
}

View File

@ -22,20 +22,17 @@ func showHelp(cmd *cobra.Command) {
} }
} }
func HandleCobraExit(cmd *cobra.Command, err error) { func HandleCobraExit(cmd *cobra.Command, err error) error {
if err == nil { if err == nil {
ok, err := cmd.Flags().GetBool(_c.HelpCommandName) ok, err := cmd.PersistentFlags().GetBool(_c.HelpCommandName)
if cmd.Name() == _c.HelpCommandName || err == nil && ok { if cmd.Name() == _c.HelpCommandName || err == nil && ok {
os.Exit(statuscode.RenderHelp) os.Exit(statuscode.RenderHelp)
} }
os.Exit(42) os.Exit(statuscode.Ok)
} }
switch tErr := err.(type) { switch err.(type) {
case SubCommandExit:
logrus.Debugf("Sub-command failed with: %s", err.Error())
os.Exit(tErr.ExitCode)
case BadArguments: case BadArguments:
showHelp(cmd) showHelp(cmd)
logrus.Error(err) logrus.Error(err)
@ -44,13 +41,6 @@ func HandleCobraExit(cmd *cobra.Command, err error) {
showHelp(cmd) showHelp(cmd)
logrus.Error(err) logrus.Error(err)
os.Exit(statuscode.NotFound) os.Exit(statuscode.NotFound)
case ConfigError:
showHelp(cmd)
logrus.Error(err)
os.Exit(statuscode.ConfigError)
case EnvironmentError:
logrus.Error(err)
os.Exit(statuscode.ConfigError)
default: default:
if strings.HasPrefix(err.Error(), "unknown command") { if strings.HasPrefix(err.Error(), "unknown command") {
showHelp(cmd) showHelp(cmd)
@ -64,4 +54,5 @@ func HandleCobraExit(cmd *cobra.Command, err error) {
logrus.Errorf("Unknown error: %s", err) logrus.Errorf("Unknown error: %s", err)
os.Exit(2) os.Exit(2)
return err
} }

View File

@ -132,7 +132,6 @@ func TestSilent(t *testing.T) {
t.Fail() t.Fail()
} }
}) })
} }
func TestEnvironmentMapEnabled(t *testing.T) { func TestEnvironmentMapEnabled(t *testing.T) {