cli

package
v0.19.7 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 5 Imported by: 0

README

cli

import "github.com/kitsunium/sdk/pkg/v1/app/cli"

Package cli is the public facade for the SDK's command-line domain: the sub-commands, the generated help and the typed exit status that the stdlib's flag package deliberately does not have.

root := cli.Command{
	Name:    "tool",
	Summary: "does the thing",
	Commands: []cli.Command{{
		Name:    "serve",
		Summary: "run the HTTP server",
		Flags:   func(fs *flag.FlagSet) { fs.IntVar(&port, "port", 8080, "listen `port`") },
		Run: func(ctx context.Context, in cli.Invocation) error {
			return serve(ctx, port)
		},
	}},
}

func main() {
	app, err := cli.New(cli.Config{}, root)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(cli.Status(err))
	}
	err = app.Execute(context.Background(), os.Args[1:])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	os.Exit(cli.Status(err))
}
What it adds to package flag, and what it leaves alone

It adds four things and nothing else: sub-commands to arbitrary depth, one help text generated from the declarations, a typed exit status, and a seam to the rest of the SDK.

It leaves the whole of flag alone. A Binder receives the stdlib's own *flag.FlagSet: the flag syntax, the flag.Value interface, every FooVar helper you already use, the "-x=v / -x v" forms and the rendering of the defaults table are flag's, unchanged and unwrapped. There is no cobra, no pflag, no third-party dependency of any kind — an argument parser is a mechanism, not a connector to somebody else's system, and this SDK writes its mechanisms.

Nothing here can end your process

flag's own answer to a bad argument is flag.ExitOnError, which calls os.Exit from inside a library: it makes the parse untestable, it skips every deferred function in the program, and it decides on your behalf whether the process should still be alive. This domain uses flag.ContinueOnError and returns an error. There is no os.Exit, no log.Fatal and no panic in the path — pinned by an AST audit over the production sources AND the suite, not by a comment.

The exit status

The status is carried by the SDK's existing error model and NOT by a second convention: a sentinel declares it with errs.WithExitCode and a caller reads it with errs.ExitCodeOf. Status is the single guard on top of that, and it exists for one sharp reason: errs.ExitCodeOf(nil) is 70, because it answers "what status does this error map to" and nil is not an error. Passing a successful Execute straight into it would exit 70 on every success.

| outcome                          | status | who chose it              |
|----------------------------------|--------|---------------------------|
| the command succeeded            | 0      | Status                    |
| -h / -help was asked for         | 0      | Execute returns nil       |
| the command line was wrong       | 64     | service/app/cli (EX_USAGE)    |
| the tree is wired wrong          | 78     | core/app/cli (EX_CONFIG)      |
| the command failed               | its own errs code, or 70   | the command   |

The last row is the one worth stating: an Action returning an *errs.Error keeps its own code AND its own exit status all the way out, because the error is returned VERBATIM and never relabelled by the framework that called it.

Help

The help is generated from the same declarations the resolver walks — the summaries, the sub-command list, and flag's own PrintDefaults over the set the Binder filled. It is never written by hand, because a help maintained beside the declarations eventually describes a flag that was renamed, and an operator acts on what the help says.

Asking for it is not a failure: -h writes the help and Execute returns nil. A bad command line writes the SAME help and returns an EX_USAGE error, so the exit status is what distinguishes them and the text does not have to.

Where the output goes

Config.Output (a command's own output) and Config.ErrOutput (help and usage) both default to os.Stderr, and neither defaults to os.Stdout. ADR 0030 makes stdout a protocol channel that no SDK default may claim; a tool that emits a document sets Output: os.Stdout in main, on one visible line, and its help still stays on stderr where it cannot corrupt the document.

What it composes rather than reimplements
  • config — FlagSource turns the flags an operator ACTUALLY TYPED into the top layer of a config load. It uses flag.FlagSet.Visit and never VisitAll, so an unset flag's Go default never overrides a file. There is no env reading, no file reading, no layering and no validation here: that is the config domain, and it already exists.
  • lifecycle — nothing. There is no Signals field and no shutdown budget: signals are a process-wide side effect and lifecycle.Run already makes them opt-in for that reason. A long-running command composes it inside its own Action, where the context it needs already is.
  • errs — the whole error model, including the exit status. This domain defines no status convention of its own.
Deliberately absent

No "did you mean …?": the help that has just been written already lists every name that would have worked, which is the complete answer and cannot be wrong. No prefix or abbreviation matching, and no case folding: both make what an existing command line MEANS depend on which siblings exist in today's release. No shell-completion generator, no colour, no interactive prompting, no environment-variable fallback per flag.

Package cli — hosts the compile-time interface assertions, keeping them out of the production source so the runtime binary carries no diagnostic-only declarations.

Index

Constants

const Success int = 0

Success is the process exit status of a command that did what it was asked.

Variables

var AmbiguousCommand *errs.Error

AmbiguousCommand refuses a command carrying BOTH an Action and sub-commands, where adding a child would silently change what an existing command line means.

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.AmbiguousCommand.

Code 0.2.32.2 · reason AMBIGUOUS_COMMAND · public "A command may declare an action or sub-commands, not both"

var CommandPanicked *errs.Error

CommandPanicked is returned when an Action panicked. The recovered value and the originating stack travel as fields; the status stays EX_SOFTWARE (70).

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.CommandPanicked.

Code 0.3.62.4 · reason COMMAND_PANICKED · public "The command panicked and was recovered"

var DuplicateCommand *errs.Error

DuplicateCommand refuses two sub-commands of one group sharing a name.

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.DuplicateCommand.

Code 0.2.32.3 · reason DUPLICATE_COMMAND · public "Two sub-commands are declared under the same name"

var HelpWriteFailed *errs.Error

HelpWriteFailed is returned when -h asked for the help and the stream it goes to did not take the page — a closed pipe, a full disk. The stream's own error stays matchable beneath it, or rides in a field when it is itself an SDK error, so the code and EX_IOERR (74) are always this one's. A bad command line keeps its EX_USAGE even when its help could not be written.

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.HelpWriteFailed.

Code 0.3.62.5 · reason HELP_WRITE_FAILED · public "The help could not be written"

var InvalidCommand *errs.Error

InvalidCommand refuses a declaration that could never be reached or could never run: a blank name, a name that would be parsed as a flag, a child with no summary, or neither an Action nor sub-commands.

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.InvalidCommand.

Code 0.2.32.1 · reason INVALID_COMMAND · public "The command declaration is not runnable and was refused"

var InvalidFlags *errs.Error

InvalidFlags is returned when package flag refused the vector. EX_USAGE (64).

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.InvalidFlags.

Code 0.3.62.3 · reason INVALID_FLAGS · public "The flags for this command could not be parsed"

var MissingCommand *errs.Error

MissingCommand is returned when a group is invoked with no sub-command. EX_USAGE (64).

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.MissingCommand.

Code 0.3.62.2 · reason MISSING_COMMAND · public "This command needs a sub-command"

var ReservedFlag *errs.Error

ReservedFlag refuses a Binder that bound -h or -help, the two names the SDK answers itself.

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.ReservedFlag.

Code 0.2.32.4 · reason RESERVED_FLAG · public "The flag names -h and -help are answered by the SDK"

var UnknownCommand *errs.Error

UnknownCommand is returned when a token names no sub-command of the group it was typed under. EX_USAGE (64).

Initialised with github.com/kitsunium/sdk/internal/core/app/cli.UnknownCommand.

Code 0.3.62.1 · reason UNKNOWN_COMMAND · public "That sub-command does not exist"

func Execute

func Execute(ctx context.Context, cfg Config, root Command, args []string) error

Execute is the one-line form of New followed by Executor.Execute. It returns the same errors both would: a construction refusal carries EX_CONFIG (78), so a caller that only wants a status can pass the result straight to Status.

func FlagSource

func FlagSource(invocation Invocation) *FlagSourceValue

FlagSource turns the flags an operator ACTUALLY TYPED on one invocation into a config layer, for use as the LAST source of a config.Load or config.LoadSchema — the position that makes a flag win over a file and over the environment.

It walks flag.FlagSet.Visit and never VisitAll: an unset flag contributes NOTHING, so a flag whose default is the Go zero cannot silently override the configuration file on every run. The flag name is the config key verbatim, with no case or separator transformation, because a rename rule is a second grammar whose failure mode is a key that quietly matches nothing.

func New

func New(cfg Config, root Command) (runner Executor, err error)

New validates a whole command tree and returns the Executor that runs it.

The WHOLE tree is checked, not the branch an invocation happens to take, so a mis-wired `tool db migrate` fails on the first run of the binary rather than on the first run of the migration. Every refusal carries EX_CONFIG (78).

Each Binder is CALLED once here, on a throwaway flag set, which is what proves it binds no reserved name — so a Binder must be safe to call more than once and must touch only the set it is given.

func Status

func Status(err error) int

Status is the process exit status for what Executor.Execute returned: 0 when err is nil, and errs.ExitCodeOf(err) otherwise.

It is a guard on the SDK's existing convention and not a second one. errs.ExitCodeOf answers "what status does THIS ERROR map to", so it returns the EX_SOFTWARE default (70) for a nil it was never meant to be handed — which is correct for an accessor and catastrophic at a CLI boundary, where success is the common case. This is the one place in the SDK where nil has to mean 0, so this is where the guard lives.

os.Exit(cli.Status(app.Execute(ctx, os.Args[1:])))

type Action

Action is what one command does — see Command.

type Action = corecli.Action

Action is an alias of github.com/kitsunium/sdk/internal/core/app/cli.Action, declared as:

type Action func(ctx context.Context, invocation InvocationValue) error

type Binder

Binder declares one command's flags onto the stdlib set that parses them.

type Binder = corecli.Binder

Binder is an alias of github.com/kitsunium/sdk/internal/core/app/cli.Binder, declared as:

type Binder func(flags *flag.FlagSet)

type Command

Command is one declared command: a name, what it says about itself, its flags, and EITHER an Action OR sub-commands.

type Command = corecli.CommandValue

Command is an alias of github.com/kitsunium/sdk/internal/core/app/cli.CommandValue, declared as:

type Command struct {
	// Name is the single token an operator types to reach this command. It is
	// compared by exact byte equality — there is deliberately no abbreviation
	// matching and no case folding, because both make the meaning of an
	// existing command line depend on which siblings exist today.
	//
	// It must be non-empty, must contain no space, and must not begin with
	// "-": a name beginning with "-" could never be typed, since the parent's
	// flag parse would consume it as a flag and stop before it.
	Name        string
	// Summary is the one-line description listed beside this command in its
	// parent's help. It is required for a child, because a command listed with
	// no summary is a name an operator has to guess at.
	Summary     string
	// Description is the optional long form shown in this command's OWN help,
	// under the usage line. It may span several lines; it is written verbatim.
	Description string
	// Flags declares this command's flags. A nil Binder is a working
	// declaration and means "this command takes no flags" — unlike a nil Run,
	// which is a real absence, a command with no flags is an ordinary command.
	Flags       Binder
	// Run is what a LEAF does. Exactly one of Run and Commands is set.
	Run         Action
	// Commands are a GROUP's children, in the order the help lists them. Two
	// children may not share a Name.
	Commands    []CommandValue
}

func (Command) IsGroup
func (Command) IsGroup() bool

IsGroup reports whether this command dispatches to children rather than running an Action. It reads the declaration rather than a flag set at construction, so it answers the same on a CommandValue the caller has just written and on one the engine has already validated.

type Config

Config configures New — two writers, and deliberately nothing else.

type Config = svccli.Config

Config is an alias of github.com/kitsunium/sdk/internal/service/app/cli.Config, declared as:

type Config struct {
	// Output is where a command's OWN output goes. It reaches a [corecli.Action] as
	// InvocationValue.Output, so a command never has to reach for a process
	// stream itself.
	//
	// The zero value resolves to os.Stderr, NOT os.Stdout. ADR 0030 makes
	// stdout a protocol channel that no SDK default may claim: a tool whose
	// output is a JSON document on a pipe sets Output: os.Stdout in main,
	// which is one visible line at the one place that can honestly make the
	// choice. A zero value is what somebody who has not yet learned the
	// question exists chose, so it must not be the dangerous one.
	Output    io.Writer
	// ErrOutput is where the generated help, and the usage that follows a bad
	// command line, are written. The zero value resolves to os.Stderr.
	//
	// It is a SECOND writer rather than the same one so that setting Output to
	// os.Stdout for a machine-readable tool does not put a help page in the
	// middle of the document a consumer is parsing.
	//
	// What is deliberately NOT written here is the error itself. The engine
	// owns the help — it is generated from the declarations and only the SDK
	// can render it — and the caller owns how an error is presented, because a
	// caller may want it as a line on stderr, as a log record, or as JSON. Two
	// voices printing one failure is how a CLI ends up saying it twice.
	ErrOutput io.Writer
}

type Executor

Executor resolves an argument vector against a command tree and runs it.

type Executor = corecli.Executor

Executor is an alias of github.com/kitsunium/sdk/internal/core/app/cli.Executor, declared as:

type Executor interface {
	// Execute resolves args against the tree and runs the command it names.
	// args is the vector WITHOUT the program name — os.Args[1:] at a call
	// site — because a library that reads os.Args itself cannot be tested and
	// cannot be embedded.
	//
	// It returns nil when the command succeeded AND when help was requested,
	// because asking for help is not a failure. Every other outcome is an
	// *errs.Error whose exit status the caller reads with errs.ExitCodeOf.
	Execute(ctx context.Context, args []string) error
}

func (Executor) Execute
func (Executor) Execute(ctx context.Context, args []string) error

Execute resolves args against the tree and runs the command it names. args is the vector WITHOUT the program name — os.Args[1:] at a call site — because a library that reads os.Args itself cannot be tested and cannot be embedded.

It returns nil when the command succeeded AND when help was requested, because asking for help is not a failure. Every other outcome is an *errs.Error whose exit status the caller reads with errs.ExitCodeOf.

type FlagSourceValue

FlagSourceValue is one invocation's typed flags as a config layer.

type FlagSourceValue = svccli.FlagSourceValue

FlagSourceValue is an alias of github.com/kitsunium/sdk/internal/service/app/cli.FlagSourceValue, declared as:

type FlagSourceValue struct{}

func (FlagSourceValue) Load
func (*FlagSourceValue) Load() (values map[string]any, err error)

Load returns the layer. It satisfies internal/core/app/config.Source.

type Invocation

Invocation is one resolved command line as an Action sees it.

type Invocation = corecli.InvocationValue

Invocation is an alias of github.com/kitsunium/sdk/internal/core/app/cli.InvocationValue, declared as:

type Invocation struct {
	// Path is the resolved command path, root first — {"tool", "db",
	// "migrate"}. It is what the help and every error message name the command
	// by, so a message and a shell prompt spell the same thing.
	Path   []string
	// Args are the positional arguments left after the LEAF's flags were
	// parsed: flag.FlagSet.Args of the last set in Flags.
	//
	// It is nil when there were none, which is Go's ordinary spelling — len
	// and range answer identically for a nil and an empty slice, and manufacturing
	// an empty one would only hide that distinction from a caller who does not
	// need it.
	Args   []string
	// Flags are the flag sets that parsed this invocation, root first, one per
	// element of Path; the leaf's own set is the last. A group's flags are
	// parsed by the group's set before its child's name is even read, which is
	// why they are separate sets and not one merged one.
	//
	// The stdlib's set is handed over unchanged rather than wrapped, so
	// Lookup, Visit, VisitAll, NFlag and Arg all work exactly as their
	// documentation says. Calling Parse on one again is not defended against:
	// the engine has already parsed it, and re-parsing it is a caller's bug
	// the SDK cannot distinguish from a caller's intent.
	Flags  []*flag.FlagSet
	// Output is the writer this command may print to. It is never nil.
	//
	// It is NOT os.Stdout unless the caller said so: ADR 0030 makes stdout a
	// protocol channel that no SDK default may claim, so the zero
	// configuration resolves this to os.Stderr and a tool whose output IS a
	// protocol sets it explicitly, in main, on one visible line. An Action is
	// handed the writer precisely so it never has to reach for a stream
	// itself.
	Output io.Writer
}

func (Invocation) Leaf
func (Invocation) Leaf() *flag.FlagSet

Leaf returns the flag set of the command that is actually running — the last element of [InvocationValue.Flags] — or nil when the command declared no flags anywhere on its path.

It exists so a caller reading its own flags does not index a slice whose length is a property of how deep the command happens to sit in the tree.

Generated from docs/api by tools/genindex.

Documentation

Overview

Package cli is the public facade for the SDK's command-line domain: the sub-commands, the generated help and the typed exit status that the stdlib's flag package deliberately does not have.

root := cli.Command{
	Name:    "tool",
	Summary: "does the thing",
	Commands: []cli.Command{{
		Name:    "serve",
		Summary: "run the HTTP server",
		Flags:   func(fs *flag.FlagSet) { fs.IntVar(&port, "port", 8080, "listen `port`") },
		Run: func(ctx context.Context, in cli.Invocation) error {
			return serve(ctx, port)
		},
	}},
}

func main() {
	app, err := cli.New(cli.Config{}, root)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(cli.Status(err))
	}
	err = app.Execute(context.Background(), os.Args[1:])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	os.Exit(cli.Status(err))
}

What it adds to package flag, and what it leaves alone

It adds four things and nothing else: sub-commands to arbitrary depth, one help text generated from the declarations, a typed exit status, and a seam to the rest of the SDK.

It leaves the whole of flag alone. A Binder receives the stdlib's own *flag.FlagSet: the flag syntax, the flag.Value interface, every FooVar helper you already use, the "-x=v / -x v" forms and the rendering of the defaults table are flag's, unchanged and unwrapped. There is no cobra, no pflag, no third-party dependency of any kind — an argument parser is a mechanism, not a connector to somebody else's system, and this SDK writes its mechanisms.

Nothing here can end your process

flag's own answer to a bad argument is flag.ExitOnError, which calls os.Exit from inside a library: it makes the parse untestable, it skips every deferred function in the program, and it decides on your behalf whether the process should still be alive. This domain uses flag.ContinueOnError and returns an error. There is no os.Exit, no log.Fatal and no panic in the path — pinned by an AST audit over the production sources AND the suite, not by a comment.

The exit status

The status is carried by the SDK's existing error model and NOT by a second convention: a sentinel declares it with errs.WithExitCode and a caller reads it with errs.ExitCodeOf. Status is the single guard on top of that, and it exists for one sharp reason: errs.ExitCodeOf(nil) is 70, because it answers "what status does this error map to" and nil is not an error. Passing a successful Execute straight into it would exit 70 on every success.

| outcome                          | status | who chose it              |
|----------------------------------|--------|---------------------------|
| the command succeeded            | 0      | Status                    |
| -h / -help was asked for         | 0      | Execute returns nil       |
| the command line was wrong       | 64     | service/app/cli (EX_USAGE)    |
| the tree is wired wrong          | 78     | core/app/cli (EX_CONFIG)      |
| the command failed               | its own errs code, or 70   | the command   |

The last row is the one worth stating: an Action returning an *errs.Error keeps its own code AND its own exit status all the way out, because the error is returned VERBATIM and never relabelled by the framework that called it.

Help

The help is generated from the same declarations the resolver walks — the summaries, the sub-command list, and flag's own PrintDefaults over the set the Binder filled. It is never written by hand, because a help maintained beside the declarations eventually describes a flag that was renamed, and an operator acts on what the help says.

Asking for it is not a failure: -h writes the help and Execute returns nil. A bad command line writes the SAME help and returns an EX_USAGE error, so the exit status is what distinguishes them and the text does not have to.

Where the output goes

Config.Output (a command's own output) and Config.ErrOutput (help and usage) both default to os.Stderr, and neither defaults to os.Stdout. ADR 0030 makes stdout a protocol channel that no SDK default may claim; a tool that emits a document sets Output: os.Stdout in main, on one visible line, and its help still stays on stderr where it cannot corrupt the document.

What it composes rather than reimplements

  • config — FlagSource turns the flags an operator ACTUALLY TYPED into the top layer of a config load. It uses flag.FlagSet.Visit and never VisitAll, so an unset flag's Go default never overrides a file. There is no env reading, no file reading, no layering and no validation here: that is the config domain, and it already exists.
  • lifecycle — nothing. There is no Signals field and no shutdown budget: signals are a process-wide side effect and lifecycle.Run already makes them opt-in for that reason. A long-running command composes it inside its own Action, where the context it needs already is.
  • errs — the whole error model, including the exit status. This domain defines no status convention of its own.

Deliberately absent

No "did you mean …?": the help that has just been written already lists every name that would have worked, which is the complete answer and cannot be wrong. No prefix or abbreviation matching, and no case folding: both make what an existing command line MEANS depend on which siblings exist in today's release. No shell-completion generator, no colour, no interactive prompting, no environment-variable fallback per flag.

Package cli — hosts the compile-time interface assertions, keeping them out of the production source so the runtime binary carries no diagnostic-only declarations.

Index

Constants

View Source
const Success int = 0

Success is the process exit status of a command that did what it was asked.

Variables

View Source
var (
	// InvalidCommand refuses a declaration that could never be reached or
	// could never run: a blank name, a name that would be parsed as a flag, a
	// child with no summary, or neither an Action nor sub-commands.
	InvalidCommand = corecli.InvalidCommand
	// AmbiguousCommand refuses a command carrying BOTH an Action and
	// sub-commands, where adding a child would silently change what an
	// existing command line means.
	AmbiguousCommand = corecli.AmbiguousCommand
	// DuplicateCommand refuses two sub-commands of one group sharing a name.
	DuplicateCommand = corecli.DuplicateCommand
	// ReservedFlag refuses a Binder that bound -h or -help, the two names the
	// SDK answers itself.
	ReservedFlag = corecli.ReservedFlag

	// UnknownCommand is returned when a token names no sub-command of the
	// group it was typed under. EX_USAGE (64).
	UnknownCommand = corecli.UnknownCommand
	// MissingCommand is returned when a group is invoked with no sub-command.
	// EX_USAGE (64).
	MissingCommand = corecli.MissingCommand
	// InvalidFlags is returned when package flag refused the vector. EX_USAGE
	// (64).
	InvalidFlags = corecli.InvalidFlags
	// CommandPanicked is returned when an [Action] panicked. The recovered
	// value and the originating stack travel as fields; the status stays
	// EX_SOFTWARE (70).
	CommandPanicked = corecli.CommandPanicked
	// HelpWriteFailed is returned when -h asked for the help and the stream it
	// goes to did not take the page — a closed pipe, a full disk. The
	// stream's own error stays matchable beneath it, or rides in a field when
	// it is itself an SDK error, so the code and EX_IOERR (74) are always this
	// one's. A bad command line keeps its EX_USAGE even when its help could
	// not be written.
	HelpWriteFailed = corecli.HelpWriteFailed
)

Functions

func Execute

func Execute(ctx context.Context, cfg Config, root Command, args []string) error

Execute is the one-line form of New followed by Executor.Execute. It returns the same errors both would: a construction refusal carries EX_CONFIG (78), so a caller that only wants a status can pass the result straight to Status.

func Status

func Status(err error) int

Status is the process exit status for what Executor.Execute returned: 0 when err is nil, and errs.ExitCodeOf(err) otherwise.

It is a guard on the SDK's existing convention and not a second one. errs.ExitCodeOf answers "what status does THIS ERROR map to", so it returns the EX_SOFTWARE default (70) for a nil it was never meant to be handed — which is correct for an accessor and catastrophic at a CLI boundary, where success is the common case. This is the one place in the SDK where nil has to mean 0, so this is where the guard lives.

os.Exit(cli.Status(app.Execute(ctx, os.Args[1:])))

Types

type Action

type Action = corecli.Action

Action is what one command does — see Command.

type Binder

type Binder = corecli.Binder

Binder declares one command's flags onto the stdlib set that parses them.

type Command

type Command = corecli.CommandValue

Command is one declared command: a name, what it says about itself, its flags, and EITHER an Action OR sub-commands.

type Config

type Config = svccli.Config

Config configures New — two writers, and deliberately nothing else.

type Executor

type Executor = corecli.Executor

Executor resolves an argument vector against a command tree and runs it.

func New

func New(cfg Config, root Command) (runner Executor, err error)

New validates a whole command tree and returns the Executor that runs it.

The WHOLE tree is checked, not the branch an invocation happens to take, so a mis-wired `tool db migrate` fails on the first run of the binary rather than on the first run of the migration. Every refusal carries EX_CONFIG (78).

Each Binder is CALLED once here, on a throwaway flag set, which is what proves it binds no reserved name — so a Binder must be safe to call more than once and must touch only the set it is given.

type FlagSourceValue

type FlagSourceValue = svccli.FlagSourceValue

FlagSourceValue is one invocation's typed flags as a config layer.

func FlagSource

func FlagSource(invocation Invocation) *FlagSourceValue

FlagSource turns the flags an operator ACTUALLY TYPED on one invocation into a config layer, for use as the LAST source of a config.Load or config.LoadSchema — the position that makes a flag win over a file and over the environment.

It walks flag.FlagSet.Visit and never VisitAll: an unset flag contributes NOTHING, so a flag whose default is the Go zero cannot silently override the configuration file on every run. The flag name is the config key verbatim, with no case or separator transformation, because a rename rule is a second grammar whose failure mode is a key that quietly matches nothing.

type Invocation

type Invocation = corecli.InvocationValue

Invocation is one resolved command line as an Action sees it.

Jump to

Keyboard shortcuts

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