diff --git a/cmd/compose/bridge.go b/cmd/compose/bridge.go index 64620c9078..f3b14c6c2d 100644 --- a/cmd/compose/bridge.go +++ b/cmd/compose/bridge.go @@ -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), @@ -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() + 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{ @@ -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), diff --git a/cmd/compose/bridge_test.go b/cmd/compose/bridge_test.go index 09a3fdf46c..d7864ff5d7 100644 --- a/cmd/compose/bridge_test.go +++ b/cmd/compose/bridge_test.go @@ -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 { @@ -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) + } }) } } diff --git a/docs/reference/docker_compose_bridge.yaml b/docs/reference/docker_compose_bridge.yaml index 5ef9ebf558..86acb68f9f 100644 --- a/docs/reference/docker_compose_bridge.yaml +++ b/docs/reference/docker_compose_bridge.yaml @@ -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: diff --git a/docs/reference/docker_compose_bridge_transformations.yaml b/docs/reference/docker_compose_bridge_transformations.yaml index 2ab5661f0b..fadf1e336c 100644 --- a/docs/reference/docker_compose_bridge_transformations.yaml +++ b/docs/reference/docker_compose_bridge_transformations.yaml @@ -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: