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
12 changes: 11 additions & 1 deletion cli/azd/cmd/auto_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/extensions"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/ioc"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/azure/azure-dev/cli/azd/pkg/update"
Expand Down Expand Up @@ -460,7 +461,9 @@ func ExecuteWithAutoInstall(ctx context.Context, rootContainer *ioc.NestedContai
// This also enables the global options to be set in the container for support during extension framework callbacks.
globalOpts := &internal.GlobalCommandOptions{}
if err := ParseGlobalFlags(os.Args[1:], globalOpts); err != nil {
result.Err = fmt.Errorf("Warning: failed to parse global flags: %w", err)
// main.go only exits on result.Err — print here so users see pre-cobra parse failures.
Comment thread
JeffreyCA marked this conversation as resolved.
fmt.Fprintln(os.Stderr, output.WithErrorFormat("ERROR: %s", err.Error()))
Comment thread
JeffreyCA marked this conversation as resolved.
result.Err = err
return result
}

Expand Down Expand Up @@ -762,6 +765,13 @@ func ParseGlobalFlags(args []string, opts *internal.GlobalCommandOptions) error
// command parser after the extension is loaded.
globalFlagSet.ParseErrorsAllowlist = pflag.ParseErrorsAllowlist{UnknownFlags: true}

// Register --output/-o on the pre-parse set only (not in CreateGlobalFlagSet, which
// would shadow per-command output.AddOutputParam and cause GetCommandFormatter to reject
// the empty default). Without this, pflag's UnknownFlags allowlist walks attached short
// values like "-o<value>" char-by-char and trips on "-e" (a known string flag), failing
// with `flag needs an argument: 'e' in -e`.
globalFlagSet.StringP("output", "o", "", "The output format (json, table, none).")

// Parse the arguments - unknown flags will be silently ignored
err := globalFlagSet.Parse(args)

Expand Down
33 changes: 33 additions & 0 deletions cli/azd/cmd/auto_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,3 +754,36 @@ func TestParseGlobalFlags_ExtensionCompatibility(t *testing.T) {
})
}
}

// TestParseGlobalFlags_OutputAttachedShortForm verifies that all common -o / --output
// attachment forms are accepted without error. Before the fix, pflag's UnknownFlags
// allowlist walked "-otable" char-by-char and tripped on the known string flag "-e",
// returning `flag needs an argument: 'e' in -e`.
func TestParseGlobalFlags_OutputAttachedShortForm(t *testing.T) {
t.Parallel()

cases := []struct {
name string
args []string
}{
{"attached", []string{"-otable"}},
{"attached-json", []string{"-ojson"}},
{"space-separated", []string{"-o", "table"}},
{"equals", []string{"-o=table"}},
{"long-space", []string{"--output", "table"}},
{"long-equals", []string{"--output=table"}},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
opts := &internal.GlobalCommandOptions{}
err := ParseGlobalFlags(tc.args, opts)
Comment thread
JeffreyCA marked this conversation as resolved.
// We only assert no error here. The -o/--output flag is registered on the
// pre-parse FlagSet solely to prevent pflag from walking attached short
// values char-by-char and failing on "-e"; the parsed value is intentionally
// not captured into GlobalCommandOptions (Cobra handles --output per command).
require.NoError(t, err, "ParseGlobalFlags must not error for args: %v", tc.args)
})
}
}
Comment thread
JeffreyCA marked this conversation as resolved.
Loading