From 2367c383538b1faffa792fb3203a05d9fa5a5c89 Mon Sep 17 00:00:00 2001 From: Pybsama <294532706+Pybsama@users.noreply.github.com> Date: Fri, 31 Jul 2026 00:45:36 +0800 Subject: [PATCH] fix(app): order help sections consistently --- CHANGELOG.md | 1 + pkg/app/usage.go | 18 +++---- pkg/app/usage_section_order_test.go | 83 +++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 pkg/app/usage_section_order_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 07e43b8f9..c1cbf7024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Bug Fixes: +- fix(app): display global flags after command-specific sections in help output. - fix(service-version): support autoclone when staging a service version. ([#1850](https://github.com/fastly/cli/pull/1850)) - fix(logging): the `placement` flag for all loggging commands can now be reset back to `null` by setting it's value to `""` when it was previously set to another value ([#1855](https://github.com/fastly/cli/pull/1855)) - fix(profile): profiles can now be created and updated with service-limited tokens, which cannot access `/current_user` ([#1856](https://github.com/fastly/cli/pull/1856)) diff --git a/pkg/app/usage.go b/pkg/app/usage.go index 4b522bb87..bab638e13 100644 --- a/pkg/app/usage.go +++ b/pkg/app/usage.go @@ -97,10 +97,6 @@ var CompactUsageTemplate = `{{define "FormatCommand" -}} {{T "OPTIONAL FLAGS"|Bold}} {{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}} {{end -}} -{{if .Context.Flags|GlobalFlags -}} -{{T "GLOBAL FLAGS"|Bold}} -{{.Context.Flags|GlobalFlags|FlagsToTwoColumns|FormatTwoColumns}} -{{end -}} {{if .Context.Args -}} {{T "ARGS"|Bold}} {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} @@ -115,6 +111,10 @@ var CompactUsageTemplate = `{{define "FormatCommand" -}} {{T "COMMANDS"|Bold}} {{.App.Commands|CommandsToTwoColumns|FormatTwoColumns}} {{end -}} +{{if .Context.Flags|GlobalFlags -}} +{{T "GLOBAL FLAGS"|Bold}} +{{.Context.Flags|GlobalFlags|FlagsToTwoColumns|FormatTwoColumns}} +{{end -}} ` + authGuideTemplate + ` {{T "SEE ALSO"|Bold}} {{.Context.SelectedCommand|SeeAlso}} @@ -213,11 +213,7 @@ const VerboseUsageTemplate = `{{define "FormatCommands" -}} {{else}} {{- T "USAGE"|Bold}} {{template "FormatUsage" .App -}} -{{end -}} -{{if .Context.Flags|GlobalFlags }} -{{T "GLOBAL FLAGS"|Bold}} -{{.Context.Flags|GlobalFlags|FlagsToTwoColumns|FormatTwoColumns}} -{{end -}} +{{end}} {{if .Context.Args -}} {{T "ARGS"|Bold}} {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} @@ -231,6 +227,10 @@ const VerboseUsageTemplate = `{{define "FormatCommands" -}} {{T "COMMANDS"|Bold -}} {{template "FormatCommands" .App}} {{end -}} +{{if .Context.Flags|GlobalFlags }} +{{T "GLOBAL FLAGS"|Bold}} +{{.Context.Flags|GlobalFlags|FlagsToTwoColumns|FormatTwoColumns}} +{{end -}} ` + authGuideTemplate + ` {{T "SEE ALSO"|Bold}} {{.Context.SelectedCommand|SeeAlso}} diff --git a/pkg/app/usage_section_order_test.go b/pkg/app/usage_section_order_test.go new file mode 100644 index 000000000..d421c948a --- /dev/null +++ b/pkg/app/usage_section_order_test.go @@ -0,0 +1,83 @@ +package app_test + +import ( + "bytes" + stderrors "errors" + "io" + "strings" + "testing" + + "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/testutil" +) + +func TestHelpSectionOrder(t *testing.T) { + tests := []struct { + name string + args string + sections []string + }{ + { + name: "compact category help", + args: "service --help", + sections: []string{"USAGE", "COMMANDS", "GLOBAL FLAGS", "SEE ALSO"}, + }, + { + name: "compact root help", + args: "--help", + sections: []string{"USAGE", "COMMANDS", "GLOBAL FLAGS", "SEE ALSO"}, + }, + { + name: "verbose category help", + args: "help service", + sections: []string{"USAGE", "SUBCOMMANDS", "GLOBAL FLAGS", "SEE ALSO"}, + }, + { + name: "verbose root help", + args: "help", + sections: []string{"USAGE", "COMMANDS", "GLOBAL FLAGS", "SEE ALSO"}, + }, + { + name: "compact leaf help", + args: "compute publish --help", + sections: []string{"USAGE", "OPTIONAL FLAGS", "GLOBAL FLAGS", "SEE ALSO"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var stdout bytes.Buffer + args := testutil.SplitArgs(tt.args) + + previousInit := app.Init + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + return testutil.MockGlobalData(args, &stdout), nil + } + t.Cleanup(func() { + app.Init = previousInit + }) + + err := app.Run(args, nil) + + var remediation errors.RemediationError + if !stderrors.As(err, &remediation) { + t.Fatalf("expected help output in a remediation error, got %v", err) + } + + output := remediation.Prefix + stdout.String() + previousIndex := -1 + for _, section := range tt.sections { + index := strings.Index(output, section) + if index < 0 { + t.Fatalf("missing section %q:\n%s", section, output) + } + if index <= previousIndex { + t.Fatalf("sections not in order %v:\n%s", tt.sections, output) + } + previousIndex = index + } + }) + } +}