From 8d416558a32886eb3bec6c6be8a8cbc92a6d545c Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 7 Oct 2026 14:43:38 +0000 Subject: [PATCH] Use standard help handling for the version command --- docs/cli.md | 5 +++-- docs/roadmap/remediation.md | 12 ++++++++++-- internal/cli/run.go | 15 ++------------- internal/cli/version_test.go | 3 ++- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index de8d160..fb9e256 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -30,8 +30,9 @@ overlay is loaded beside the main file, not used to locate profile files. `audita version` prints `audita ` and a newline to stdout, with no application configuration or provider setup. It returns `0` on success with empty stderr. `help version`, `version -h`, and `version --help` print command -usage to stdout and return `0`; unexpected arguments and unknown flags return -`2` with a usage error on stderr. The command uses ReleaseKit's public +usage to stdout and return `0`. Help follows standard flag parsing and can +short-circuit trailing arguments. Other invalid invocations return `2` with a +usage error on stderr. The command uses ReleaseKit's public `buildinfo.Version()` accessor. It reports an injected release identifier when present, otherwise the executable's main-module version when available and not `(devel)`, and `dev` when neither source provides a release identifier. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 317d202..8ac3739 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -4,8 +4,8 @@ `3c405f0524ec6c098d00d4e371abbace7648d446`. **Scope:** ReleaseKit adoption commits after `721365e`, including CLI version reporting, local release wiring, CI delivery, tests, and documentation. -**Status:** Findings pending remediation; no implementation changes made by this -review. +**Status:** R1 addressed; R2–R3 pending. The findings below describe the reviewed +baseline; completed work is recorded under Resolution Record. ## Architecture Assessment @@ -128,3 +128,11 @@ matrix. deployment work rather than an implementation finding or source-release gate. - No tags, pushes, hosted uploads, or LLM calls were made. Remediation should remain bounded to the findings above and add no new release gates. + +## Resolution Record + +- **R1:** The version command now uses one standard flag-parsing path. Successful + help is written once to stdout, with no stderr usage, and help can short-circuit + trailing arguments. Non-help usage errors retain status `2`. Regression tests + reproduced both defects before the fix; `go test ./internal/cli ./cmd/audita` + passed afterward. The CLI contract reflects ordinary help behavior. diff --git a/internal/cli/run.go b/internal/cli/run.go index b532aeb..fb54206 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -71,26 +71,15 @@ func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int { } func runVersion(args []string, stdout, stderr io.Writer) int { - if len(args) == 1 && (args[0] == "-h" || args[0] == "--help") { - writeVersionUsage(stdout) - return 0 - } - for _, arg := range args { - if arg == "-h" || arg == "--help" { - fmt.Fprintln(stderr, "audita version: help flags cannot be combined with other arguments") - fmt.Fprintln(stderr) - writeVersionUsage(stderr) - return 2 - } - } flags := flag.NewFlagSet("version", flag.ContinueOnError) flags.SetOutput(stderr) - flags.Usage = func() { writeVersionUsage(stderr) } + flags.Usage = func() {} // Route usage after parsing so help stays on stdout. if err := flags.Parse(args); err != nil { if err == flag.ErrHelp { writeVersionUsage(stdout) return 0 } + writeVersionUsage(stderr) return 2 } if positional := flags.Args(); len(positional) > 0 { diff --git a/internal/cli/version_test.go b/internal/cli/version_test.go index c3ecf73..6637afc 100644 --- a/internal/cli/version_test.go +++ b/internal/cli/version_test.go @@ -34,6 +34,8 @@ func TestVersionCommandHelp(t *testing.T) { {name: "help version", args: []string{"help", "version"}, want: "audita version"}, {name: "short flag", args: []string{"version", "-h"}, want: "audita version"}, {name: "long flag", args: []string{"version", "--help"}, want: "audita version"}, + {name: "parser help", args: []string{"version", "--h"}, want: "audita version"}, + {name: "help short circuits", args: []string{"version", "-h", "--help", "extra"}, want: "audita version"}, } { t.Run(test.name, func(t *testing.T) { var stdout, stderr bytes.Buffer @@ -54,7 +56,6 @@ func TestVersionCommandRejectsUnexpectedArguments(t *testing.T) { for _, args := range [][]string{ {"version", "extra"}, {"version", "--unknown"}, - {"version", "--help", "extra"}, } { t.Run(strings.Join(args, " "), func(t *testing.T) { var stdout, stderr bytes.Buffer