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
8 changes: 7 additions & 1 deletion cli/commands/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,17 @@ func Section(name, desc, help string, opts ...SectionOption) mflags.Command {
desc = help
}

fs := mflags.NewFlagSet(name)
// Sections declare no flags of their own, but users routinely place global
// flags (e.g. -C/--cluster) before a section name. Tolerate unknown flags so
// the section still renders its sub-command help instead of failing to parse.
fs.AllowUnknownFlags(true)

s := &section{
name: name,
desc: desc,
help: help,
fs: mflags.NewFlagSet(name),
fs: fs,
}

for _, o := range opts {
Expand Down
79 changes: 79 additions & 0 deletions cli/commands/section_help_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package commands

import (
"bytes"
"io"
"os"
"testing"

"miren.dev/mflags"
"miren.dev/runtime/pkg/labs"
)

// TestSectionHelpWithGlobalFlag guards against MIR-1309: a value-taking global
// flag (e.g. -C/--cluster) placed before a section name must still render the
// section's sub-commands rather than falling back to top-level help or a flag
// parse error.
func TestSectionHelpWithGlobalFlag(t *testing.T) {
labs.EnableAll()

cases := [][]string{
{"auth"},
{"auth", "help"},
{"help", "auth"},
{"-C", "prod", "auth"},
{"-C", "prod", "auth", "help"},
{"-C", "prod", "auth", "--help"},
}

for _, args := range cases {
t.Run(argsName(args), func(t *testing.T) {
out := captureDispatch(t, args)

// The auth section's sub-commands must appear; top-level help would
// instead list unrelated commands like "app".
for _, want := range []string{"generate", "provider", "ci"} {
if !bytes.Contains([]byte(out), []byte(want)) {
t.Errorf("expected auth sub-command %q in output for %v, got:\n%s", want, args, out)
}
}
})
}
}

func argsName(args []string) string {
name := ""
for i, a := range args {
if i > 0 {
name += "_"
}
name += a
}
return name
}

// captureDispatch builds the full dispatcher and runs Execute with the given
// args, returning everything written to stdout.
func captureDispatch(t *testing.T, args []string) string {
t.Helper()

d := mflags.NewDispatcher("miren")
RegisterAll(d)

old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

err := d.Execute(args)

w.Close()
os.Stdout = old

if err != nil {
t.Fatalf("Execute(%v) returned error: %v", args, err)
}

var buf bytes.Buffer
io.Copy(&buf, r)
return buf.String()
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ require (
k8s.io/klog/v2 v2.130.1
miren.dev/jsonrpc3/go/jsonrpc3 v0.0.0-20260106052505-c98e2702b093
miren.dev/lbd v0.0.0-20260224020427-8914d8db2233
miren.dev/mflags v0.0.0-20260518222642-0ca7adc28461
miren.dev/mflags v0.0.0-20260709231109-a397dcbc98df
sigs.k8s.io/knftables v0.0.21
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1989,8 +1989,8 @@ miren.dev/jsonrpc3/go/jsonrpc3 v0.0.0-20260106052505-c98e2702b093 h1:Sd+M5HSUati
miren.dev/jsonrpc3/go/jsonrpc3 v0.0.0-20260106052505-c98e2702b093/go.mod h1:B4A3wSzkcSZQw6Y5opU+rk1+1lEuCWIWkAiN7TkltGE=
miren.dev/lbd v0.0.0-20260224020427-8914d8db2233 h1:9DxH7Dhnmu7hn1OA2JC5fHpLuVgAqBySws9GLNssLl4=
miren.dev/lbd v0.0.0-20260224020427-8914d8db2233/go.mod h1:+x9fy2p45csBnGUJdqxCUmzlUTCipoVDbv6zIapTgDA=
miren.dev/mflags v0.0.0-20260518222642-0ca7adc28461 h1:ETSN/0nBy1Dqpu43INesvPWoipOaC5+pfKzRjngkeO0=
miren.dev/mflags v0.0.0-20260518222642-0ca7adc28461/go.mod h1:G1eQ/upWVdO6BGT6dlh5Yqjt+9ncH5RUAKX6UKi1F9Q=
miren.dev/mflags v0.0.0-20260709231109-a397dcbc98df h1:4fo71OveODliBgU9sv2M/uepiRNAwQPJ7YTPuNffQNc=
miren.dev/mflags v0.0.0-20260709231109-a397dcbc98df/go.mod h1:G1eQ/upWVdO6BGT6dlh5Yqjt+9ncH5RUAKX6UKi1F9Q=
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
Expand Down
Loading