fix: route deprecation warnings to stderr
Summary
Cobra's c.Print() uses OutOrStderr(), which respects whatever was set via SetOut. Because NewCmdRoot was calling rootCmd.SetOut(f.IO().StdOut), every diagnostic message cobra emitted — pflag flag-deprecation warnings, "unknown help topic", usage-on-error — landed on stdout and corrupted piped command output. The user-visible failure mode reported in #8371 (closed): glab variable export --format json | jq breaks because the deprecation warning is mixed into the JSON stream.
This is the long-standing spf13/cobra#1708.
Approach
Match GitHub CLI's fix in cli/cli's pkg/cmd/root/root.go — drop the rootCmd.SetOut/SetErr wiring entirely:
- Cobra's
c.Printfalls back toos.StderrwhenOutis unset, which is the right destination for every cobra-internal Print site (deprecation warnings, unknown help topic, version-flag declaration errors, etc.). - Plumb
f.IO()explicitly intoRootHelpFunc(which writes help tostreams.StdOut) andRootUsageFunc(which writes usage tostreams.StdErr, since usage is a diagnostic).
Also fix the same bug class in internal/utils/utils.go — IsEnvVarEnabled and PrintDeprecationWarning were writing WARNING: and DEPRECATION WARNING: messages directly to os.Stdout, with the same JSON-corruption failure mode (e.g. setting NO_PROMPT=true instead of GLAB_NO_PROMPT).
Test coverage
Three regression nets in internal/commands/root_test.go:
TestRootDoesNotWireCobraOutToStdOut— structural assertion thatrootCmd.Outis not wired toStdOut. Catches direct reintroduction ofrootCmd.SetOut(f.IO().StdOut).TestDeprecationWarningStaysOffStdOut— synthetic command with a deprecated flag exercised end-to-end throughNewCmdRoot.Execute.TestAllDeprecatedFlagsRouteToStderr— walks the real command tree, finds every flag withDeprecated != ""(17 today acrossmr,issue,incident,ci,release,variable), and exercises each throughParseFlags. Auto-scales as new deprecated flags are added.
Verified by temporarily reintroducing rootCmd.SetOut(f.IO().StdOut) — all three tests + all 17 subtests fail. Reverted, everything green.
Test plan
-
go test ./internal/...passes (except the pre-existingTestGitCommonDir/worktreemacOS symlink failure, which this MR also fixes as a separate commit) -
go vet ./...clean - Manual verification of the original failure mode:
glab variable export --format json 1>out 2>err— stdout clean, deprecation on stderr - Manual verification of jq pipe:
glab mr list --mine -F json | jq 'length'— jq parses cleanly, deprecation on stderr - Manual verification of
--help(4528 bytes on stdout, 0 on stderr) and--version(12 bytes on stdout, 0 on stderr) - Manual verification of unknown subcommand:
glab totallybogus— 0 bytes stdout, error on stderr - Manual verification of
NO_PROMPT=true glab --version—DEPRECATION WARNINGnow on stderr (was previously on stdout)
Related
Closes #8371 (closed)
See also: spf13/cobra#1708