Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

### Enhancements:

- feat(help): add -h short flag for --help. ([#1918](https://github.com/fastly/cli/pull/1918))

### Dependencies:
- build(deps): `github.com/andybalholm/brotli` from 1.2.3 to 1.2.4 ([#1913](https://github.com/fastly/cli/pull/1913))
- build(deps): `github.com/mattn/go-runewidth` from 0.0.29 to 0.0.30 ([#1913](https://github.com/fastly/cli/pull/1913))
Expand Down
8 changes: 8 additions & 0 deletions pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ func configureKingpin(data *global.Data) *kingpin.Application {
// Prevent kingpin from calling os.Exit, this gives us greater control over
// error states and output control flow.
app.Terminate(nil)
// The `help` flag is not one we registered ourselves: kingpin defines it
// internally when the application is created. We look it up by name to
// attach the `-h` shorthand, and only guard with a nil check because if a
// future kingpin version renamed or removed its built-in help flag we
// would panic here at startup instead of failing gracefully.
if helpFlag := app.GetFlag("help"); helpFlag != nil {
helpFlag.Short('h')
Comment thread
Tyagiquamar marked this conversation as resolved.
}

// IMPORTANT: Kingpin doesn't support global flags.
// Any flags defined below must also be added to two other places:
Expand Down
31 changes: 31 additions & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"context"
"encoding/json"
stderrors "errors"
"io"
"os"
"strings"
Expand Down Expand Up @@ -278,6 +279,36 @@ func TestHelpJSON(t *testing.T) {
}
}

// TestShortHelpFlag verifies that `-h` displays the usage template just like `--help`.
func TestShortHelpFlag(t *testing.T) {
for _, flag := range []string{"--help", "-h"} {
t.Run(flag, func(t *testing.T) {
var stdout bytes.Buffer
args := testutil.SplitArgs(flag)
app.Init = func(_ []string, _ io.Reader) (*global.Data, error) {
return testutil.MockGlobalData(args, &stdout), nil
}
err := app.Run(args, nil)
var output string
if err != nil {
var re errors.RemediationError
var se errors.SkipExitError
if ok := stderrors.As(err, &re); ok {
output = re.Prefix
} else if ok := stderrors.As(err, &se); ok {
if re, ok := se.Err.(errors.RemediationError); ok {
output = re.Prefix
}
}
}
output += stdout.String()
if !strings.Contains(output, "USAGE") {
t.Errorf("expected usage output containing \"USAGE\", got: %s", output)
}
})
}
}

// stripTrailingSpace removes any trailing spaces from the multiline str.
func stripTrailingSpace(str string) string {
buf := bytes.NewBuffer(nil)
Expand Down
5 changes: 3 additions & 2 deletions pkg/argparser/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,9 @@ func IsHelpOnly(args []string) bool {
return len(args) > 0 && args[0] == "help"
}

// IsHelpFlagOnly indicates if the user called `fastly --help [...]`.
// IsHelpFlagOnly indicates if the user called `fastly --help [...]` or `fastly -h [...]`.
func IsHelpFlagOnly(args []string) bool {
return len(args) > 0 && args[0] == "--help"
return len(args) > 0 && (args[0] == "--help" || args[0] == "-h")
}

// IsVerboseAndQuiet indicates if the user called `fastly --verbose --quiet`.
Expand Down Expand Up @@ -338,6 +338,7 @@ func IsGlobalFlagsOnly(args []string) bool {
"--debug-mode": 0,
"--enable-sso": 0,
"--help": 0,
"-h": 0,
"--non-interactive": 0,
"-i": 0,
"--profile": 1,
Expand Down
52 changes: 52 additions & 0 deletions pkg/argparser/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ func TestIsGlobalFlagsOnly(t *testing.T) {
args: []string{"-t", "abc"},
want: true,
},
{
name: "help flag only",
args: []string{"--help"},
want: true,
},
{
name: "short help flag only",
args: []string{"-h"},
want: true,
},
{
name: "subcommand present",
args: []string{"--verbose", "version"},
Expand All @@ -45,6 +55,48 @@ func TestIsGlobalFlagsOnly(t *testing.T) {
}
}

func TestIsHelpFlagOnly(t *testing.T) {
tests := []struct {
name string
args []string
want bool
}{
{
name: "--help",
args: []string{"--help"},
want: true,
},
{
name: "-h",
args: []string{"-h"},
want: true,
},
{
name: "-h with subcommand",
args: []string{"-h", "service"},
want: true,
},
{
name: "subcommand first",
args: []string{"service", "-h"},
want: false,
},
{
name: "empty",
args: []string{},
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := argparser.IsHelpFlagOnly(tt.args); got != tt.want {
t.Errorf("IsHelpFlagOnly(%v) = %v, want %v", tt.args, got, tt.want)
}
})
}
}

func TestArgsIsHelpJSON(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading