Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit non-zero exit code upon non-runnable subcommand #922

Merged
merged 6 commits into from Aug 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitignore
Expand Up @@ -32,7 +32,8 @@ Session.vim
tags

*.exe

cobra
cobra.test

.idea/*
.idea/
*.iml
4 changes: 2 additions & 2 deletions cobra.go
Expand Up @@ -52,7 +52,7 @@ var EnableCommandSorting = true
// if the CLI is started from explorer.exe.
// To disable the mousetrap, just set this variable to blank string ("").
// Works only on Microsoft Windows.
var MousetrapHelpText string = `This is a command line tool.
var MousetrapHelpText = `This is a command line tool.

You need to open cmd.exe and run it from there.
`
Expand All @@ -61,7 +61,7 @@ You need to open cmd.exe and run it from there.
// if the CLI is started from explorer.exe. Set to 0 to wait for the return key to be pressed.
// To disable the mousetrap, just set MousetrapHelpText to blank string ("").
// Works only on Microsoft Windows.
var MousetrapDisplayDuration time.Duration = 5 * time.Second
var MousetrapDisplayDuration = 5 * time.Second

// AddTemplateFunc adds a template function that's available to Usage and Help
// template generation.
Expand Down
5 changes: 3 additions & 2 deletions cobra/cmd/init.go
Expand Up @@ -15,10 +15,11 @@ package cmd

import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"path"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var (
Expand Down
4 changes: 2 additions & 2 deletions cobra/cmd/project.go
Expand Up @@ -2,9 +2,10 @@ package cmd

import (
"fmt"
"github.com/spf13/cobra/cobra/tpl"
"os"
"text/template"

"github.com/spf13/cobra/cobra/tpl"
)

// Project contains name, license and paths to projects.
Expand All @@ -25,7 +26,6 @@ type Command struct {
}

func (p *Project) Create() error {

// check if AbsolutePath exists
if _, err := os.Stat(p.AbsolutePath); os.IsNotExist(err) {
// create directory
Expand Down
4 changes: 2 additions & 2 deletions cobra/cmd/root.go
Expand Up @@ -36,8 +36,8 @@ to quickly create a Cobra application.`,
)

// Execute executes the root command.
func Execute() {
rootCmd.Execute()
func Execute() error {
return rootCmd.Execute()
}

func init() {
Expand Down
10 changes: 8 additions & 2 deletions cobra/main.go
Expand Up @@ -13,8 +13,14 @@

package main

import "github.com/spf13/cobra/cobra/cmd"
import (
"os"

"github.com/spf13/cobra/cobra/cmd"
)

func main() {
cmd.Execute()
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
19 changes: 15 additions & 4 deletions command.go
Expand Up @@ -17,6 +17,7 @@ package cobra

import (
"bytes"
"errors"
"fmt"
"io"
"os"
Expand All @@ -27,6 +28,8 @@ import (
flag "github.com/spf13/pflag"
)

var ErrSubCommandRequired = errors.New("subcommand is required")

// FParseErrWhitelist configures Flag parse errors to be ignored
type FParseErrWhitelist flag.ParseErrorsWhitelist

Expand Down Expand Up @@ -228,7 +231,7 @@ func (c *Command) SetErr(newErr io.Writer) {
c.errWriter = newErr
}

// SetOut sets the source for input data
// SetIn sets the source for input data
// If newIn is nil, os.Stdin is used.
func (c *Command) SetIn(newIn io.Reader) {
c.inReader = newIn
Expand Down Expand Up @@ -297,7 +300,7 @@ func (c *Command) ErrOrStderr() io.Writer {
return c.getErr(os.Stderr)
}

// ErrOrStderr returns output to stderr
// InOrStdin returns output to stderr
func (c *Command) InOrStdin() io.Reader {
return c.getIn(os.Stdin)
}
Expand Down Expand Up @@ -369,7 +372,7 @@ func (c *Command) HelpFunc() func(*Command, []string) {
}
return func(c *Command, a []string) {
c.mergePersistentFlags()
err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c)
err := tmpl(c.OutOrStderr(), c.HelpTemplate(), c)
if err != nil {
c.Println(err)
}
Expand Down Expand Up @@ -786,7 +789,7 @@ func (c *Command) execute(a []string) (err error) {
}

if !c.Runnable() {
return flag.ErrHelp
return ErrSubCommandRequired
}

c.preRun()
Expand Down Expand Up @@ -920,6 +923,14 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
return cmd, nil
}

// If command wasn't runnable, show full help, but do return the error.
// This will result in apps by default returning a non-success exit code, but also gives them the option to
// handle specially.
if err == ErrSubCommandRequired {
cmd.HelpFunc()(cmd, args)
return cmd, err
}

// If root command has SilentErrors flagged,
// all subcommands should respect it
if !cmd.SilenceErrors && !c.SilenceErrors {
Expand Down
4 changes: 2 additions & 2 deletions command_test.go
Expand Up @@ -836,8 +836,8 @@ func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
rootCmd.AddCommand(childCmd)

output, err := executeCommand(rootCmd, "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
if err != ErrSubCommandRequired {
t.Errorf("Expected error")
}

checkStringContains(t, output, childCmd.Long)
Expand Down