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
11 changes: 10 additions & 1 deletion builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ func (c Command) Register() {
name := c.Name
factory := c.MakeFlags
normalize := c.NormalizeArgs
metaRegistry[name] = CommandMeta{Name: name, Description: c.Description, Help: c.Help}

// Probe whether the command registers any flags so we can record it
// in metadata (used by tests to enforce help-consistency invariants).
probe := pflag.NewFlagSet(name, pflag.ContinueOnError)
probe.SetOutput(io.Discard)
factory(probe)
hasFlags := probe.HasFlags()

metaRegistry[name] = CommandMeta{Name: name, Description: c.Description, Help: c.Help, HasFlags: hasFlags}
addToRegistry(name, func(ctx context.Context, callCtx *CallContext, args []string) Result {
fs := pflag.NewFlagSet(name, pflag.ContinueOnError)
fs.SetOutput(io.Discard) // handler formats errors itself
Expand Down Expand Up @@ -221,6 +229,7 @@ type CommandMeta struct {
Name string
Description string
Help string
HasFlags bool // true when MakeFlags registers at least one flag
}

var metaRegistry = map[string]CommandMeta{}
Expand Down
18 changes: 18 additions & 0 deletions builtins/tests/help/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"mvdan.cc/sh/v3/syntax"

"github.com/DataDog/rshell/builtins"
"github.com/DataDog/rshell/internal/interpoption"
"github.com/DataDog/rshell/interp"
)
Expand Down Expand Up @@ -290,3 +291,20 @@ func TestHelpNoStderrOnSuccess(t *testing.T) {
assert.Equal(t, 0, code)
assert.Empty(t, stderr)
}

// --- Invariant: Help field only on NoFlags commands ---

func TestHelpFieldOnlyOnNoFlagsCommands(t *testing.T) {
// Trigger registration so Names()/Meta() are populated.
_, _ = interp.New()

for _, name := range builtins.Names() {
meta, ok := builtins.Meta(name)
require.True(t, ok)
if meta.Help != "" && meta.HasFlags {
t.Errorf("%s: Help field must not be set on commands that register flags — "+
"use --help instead so that 'help %s' and '%s --help' produce the same output",
name, name, name)
}
}
}
7 changes: 1 addition & 6 deletions builtins/uname/uname.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,7 @@ import (
var Cmd = builtins.Command{
Name: "uname",
Description: "print system information",
Help: `uname: uname [-asnrvm]
Print system information.

With no flags, print the kernel name (same as -s).
Reads from /proc/sys/kernel/ (configurable via --proc-path).`,
MakeFlags: makeFlags,
MakeFlags: makeFlags,
}

// kernelFiles maps each flag letter to the proc pseudo-file that
Expand Down
Loading