Help us shape the future of Go! Take the 2026 Go Developer Survey

cli

package
v0.68.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

Documentation

Overview

Package cli carries the three things every command in this repository does the same way, so that they are decided once instead of thirty-two times.

It exists because the migration from the standard `flag` package to cobra touched every command at the same place, and that was the moment to notice that the place was copied thirty-two times: the usage text, the exit-status convention, and the staleness note.

The exit-status convention

Every command here answers with 0, 1 or 2, and the three are deliberately not collapsed into pass/fail:

0  the question was answered and the answer is clean
1  the question was answered and something was found
2  the question could NOT be answered

A lane that cannot tell 1 from 2 eventually reads a scan that could not run as a fleet with nothing wrong, which is the failure these tools exist to prevent. cobra returns an error and nothing else, so the status travels inside the error, as an *Exit.

Flag spelling

pflag accepts `--long` and `-s`; the standard `flag` package also accepted `-long`, and this fleet's call sites used that spelling everywhere. The migration therefore BREAKS them, loudly — `unknown shorthand flag: 'g' in -go` — which is the good case: the program's variable keeps its zero value and the command fails instead of quietly ignoring an argument.

One case is silent and worth knowing: when every letter of a single-dash name is itself a registered one-letter shorthand, pflag reads a cluster and succeeds. `-ab` with `-a` and `-b` registered sets both. So do not register a one-letter shorthand whose letters can spell another flag's long name.

Index

Constants

This section is empty.

Variables

View Source
var OsExit = os.Exit

OsExit is a variable so that a test can observe a status without ending the test binary.

Functions

func Compat

func Compat(c *cobra.Command, args []string, warn io.Writer) ([]string, error)

Compat rewrites the single-dash long flags the standard `flag` package accepted into the double-dash spelling pflag requires, and says it did.

WHY THIS EXISTS. The migration from `flag` to cobra changes one spelling, and it is the spelling this fleet's call sites use: `flag` accepted -max-db-age, --max-db-age and -j alike, while pflag reads one dash as ONE letter. Measured on 2026-10-18 with the tool that counts them, over every workflow in the local fleet: 16 call sites across 13 repositories would break, 13 of them calling `vulngate -go`, and that is only the lanes — scripts, READMEs and Go source hold 71 more inside this repository alone.

Without this shim the migration needs a flag day across thirteen repositories, which is the kind of change that lands half-done. With it, an old call site keeps working and is TOLD, once, on stderr, where a person running a lane will read it.

WHAT IT REFUSES TO DO. `-ab` is genuinely ambiguous when `ab` is a long flag and `a` and `b` are both registered shorthands: pflag reads a cluster, `flag` read the long name, and guessing would silently change what a lane means. Those are refused, naming both readings. Nothing else is touched: an unknown single-dash token is left exactly as it came so that pflag still produces its own error, and everything after a bare `--` is an argument, not a flag.

func Drive

func Drive(c *cobra.Command, args []string, out, errOut io.Writer) int

Drive runs a command exactly as Run does — argument shim included — and returns the status instead of exiting.

It is what every command's `run(args, out, errOut) int` now is, which does two things at once. The tests written against that signature keep working unchanged through the migration, so a conversion cannot quietly change what a command does while its tests still pass. And what those tests exercise becomes the REAL surface: pflag parsing, the compatibility shim, the exit status mapping. Before this they drove a hand-rolled FlagSet that no user ever reached.

func Err

func Err(c *cobra.Command) io.Writer

func Exec

func Exec(c *cobra.Command, args []string, out, errOut io.Writer) error

Exec is Drive for a caller that wants the ERROR rather than a status: it applies the argument shim, runs the command, and returns whatever came back without printing it.

Some commands here were written as `run(args, out) error` rather than `run(...) int`, because their failures are sentences and not categories -- a missing index.html, a directory that is not one, a version that is neither MAJOR.MINOR nor main. Their tests read the returned error, so handing those tests a status would throw away precisely what they assert.

func Found

func Found(format string, a ...any) error

Found returns the status for a question answered with a finding: the command worked, and what it looked for is there.

func FromStatus

func FromStatus(code int) error

FromStatus turns a command's old integer status into the error cobra expects, so that a `run(...) int` written before this migration keeps its body exactly as it was.

That matters more than it looks. The alternative is editing the inside of thirty-two functions to change every `return 1` and `return 2`, in files of up to eight hundred lines, where the statuses are chosen deliberately and are documented one defect at a time. Each such edit is a chance to turn a 1 into a 2 — to turn "I looked and found something" into "I could not look" — and that is precisely the confusion this repository's tools exist to prevent.

A status this does not know becomes 2, because an unrecognised status is not evidence of a clean result.

func HelpRequested

func HelpRequested(c *cobra.Command) bool

HelpRequested reports whether the run was a request for help rather than a request to do the work.

cobra prints the help and returns a nil error, which is a status of 0 — and a command whose caller reads "0, carry on" would then print its help AND do its work. The standard `flag` package made this visible as flag.ErrHelp, and the tests written against that behaviour are what caught it here.

func New

func New(use, short, long string) *cobra.Command

New returns a command with the settings every command here wants.

SilenceUsage: a measurement that failed must not bury its reason under forty lines of help. SilenceErrors: Run prints the error itself, once, with the mark the rest of the fleet uses.

func Out

func Out(c *cobra.Command) io.Writer

Out and Err are the writers a command must print through, so that a test can read what it wrote. Reaching for os.Stdout directly is what made the previous generation of these commands testable only by running them.

func Run

func Run(c *cobra.Command)

Run executes a command and exits with the status its error carries.

It is the whole of every command's main(), so that no command can invent its own mapping from an error to a status — and so that the single-dash compatibility shim is applied in exactly one place rather than thirty-two.

func Status

func Status(c *cobra.Command) int

Status executes a command, prints whatever its error has to say, and reports the status it would exit with.

A status of 1 prints nothing extra: the command's own output has already listed what was found, and repeating it on stderr makes a lane's log read as if something went wrong on top of the finding.

func Unanswerable

func Unanswerable(format string, a ...any) error

Unanswerable returns the status for a question that could not be put: an unreadable input, a missing credential, an empty population. It is NOT the same as a clean result, and that is the whole reason it has its own status.

func Wrap

func Wrap(err error) error

Wrap turns any error into an unanswerable one, for the common case of a failure on the way to the measurement.

Types

type Exit

type Exit struct {
	Code int
	Err  error
}

An Exit carries the status a command should end with, and the reason.

Err may be nil for a status that the command's own output has already explained: a table that lists what was found does not need the error repeated under it.

func (*Exit) Error

func (e *Exit) Error() string

func (*Exit) Unwrap

func (e *Exit) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL