Skip to content
Open
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
16 changes: 16 additions & 0 deletions cmd/compose/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func bridgeCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
Use: "bridge CMD [OPTIONS]",
Short: "Convert compose files into another model",
TraverseChildren: true,
RunE: rejectUnknownSubcommand,
}
cmd.AddCommand(
convertCommand(p, dockerCli),
Expand All @@ -47,6 +48,20 @@ func bridgeCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
return cmd
}

// rejectUnknownSubcommand is the RunE for a parent command that only groups
// subcommands: by default (no Run/RunE), cobra shows help for an unknown
// subcommand but exits 0.
func rejectUnknownSubcommand(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmd.Help()
}
_ = cmd.Help()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] Error from cmd.Help() silently discarded before returning StatusError

When args is non-empty, rejectUnknownSubcommand calls _ = cmd.Help() and discards the error before returning the cli.StatusError. If the cobra output writer returns an I/O error (e.g. a broken pipe), the help text won't render and the error will be silently swallowed — the caller only sees the StatusError, with no indication that help rendering failed and no help context displayed.

The conventional Go pattern is to propagate the error:

Suggested change
_ = cmd.Help()
if err := cmd.Help(); err != nil {
return err
}
Confidence Score
🟢 strong 100/100

return cli.StatusError{
StatusCode: 1,
Status: fmt.Sprintf("unknown docker command: %q", cmd.CommandPath()+" "+args[0]),
}
}

func convertCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
convertOpts := bridge.ConvertOptions{}
cmd := &cobra.Command{
Expand Down Expand Up @@ -81,6 +96,7 @@ func transformersCommand(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "transformations CMD [OPTIONS]",
Short: "Manage transformation images",
RunE: rejectUnknownSubcommand,
}
cmd.AddCommand(
listTransformersCommand(dockerCli),
Expand Down
28 changes: 27 additions & 1 deletion cmd/compose/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ func TestBridgeCommandsArgsValidation(t *testing.T) {
args: []string{"extra"},
wantErr: "unknown command",
},
{
name: "bridge rejects an unknown subcommand",
cmd: bridgeCommand(&ProjectOptions{}, nil),
args: []string{"zzz"},
wantErr: "unknown docker command",
},
{
name: "bridge with no subcommand shows help",
cmd: bridgeCommand(&ProjectOptions{}, nil),
args: []string{},
},
{
name: "transformations rejects an unknown subcommand",
cmd: bridgeCommand(&ProjectOptions{}, nil),
args: []string{"transformations", "zzz"},
wantErr: "unknown docker command",
},
{
name: "transformations with no subcommand shows help",
cmd: transformersCommand(nil),
args: []string{},
},
}

for _, test := range tests {
Expand All @@ -63,7 +85,11 @@ func TestBridgeCommandsArgsValidation(t *testing.T) {
test.cmd.SetOut(io.Discard)
test.cmd.SetErr(io.Discard)
err := test.cmd.Execute()
assert.ErrorContains(t, err, test.wantErr)
if test.wantErr == "" {
assert.NilError(t, err)
} else {
assert.ErrorContains(t, err, test.wantErr)
}
})
}
}
1 change: 1 addition & 0 deletions docs/reference/docker_compose_bridge.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
command: docker compose bridge
short: Convert compose files into another model
long: Convert compose files into another model
usage: docker compose bridge CMD [OPTIONS]
pname: docker compose
plink: docker_compose.yaml
cname:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
command: docker compose bridge transformations
short: Manage transformation images
long: Manage transformation images
usage: docker compose bridge transformations CMD [OPTIONS]
pname: docker compose bridge
plink: docker_compose_bridge.yaml
cname:
Expand Down