Documentation
¶
Overview ¶
Package command holds the gotools commands.
A Command declares itself to urfave/cli: Build returns the *urfave.Command, with its flags, validators, positional arguments, prose and examples, and its Before, Action and After steps (written as a Spec) bound to the Context it works on. urfave/cli parses and validates and renders help; the command supplies only what is its own. The Context carries the streams, environment and seams (go runner, lock, go.mod parser, tool manager), so a test builds a Context with in-memory streams and fakes, builds the command, and runs it through urfave/cli exactly as the binary does.
Index ¶
- Constants
- func Prose(description, program string) string
- func RunRoot(ctx context.Context, root *urfave.Command, args []string) error
- func UsageError(_ context.Context, c *urfave.Command, err error, _ bool) error
- type AdoptParams
- type Check
- type Command
- type Commander
- type Config
- type Context
- func (c *Context) Adopt(ctx context.Context, p AdoptParams) error
- func (c *Context) Executable() (string, error)
- func (c *Context) ExecutablePaths() (string, string, error)
- func (c *Context) Flush() error
- func (c *Context) Install(ctx context.Context, p InstallParams) error
- func (c *Context) Layout() strategy.Layout
- func (c *Context) Load(_ context.Context, opts ...config.Option) error
- func (c *Context) LoadOrDefaults(ctx context.Context, opts ...config.Option) string
- func (c *Context) ManagedAs(t modparser.Spec) (string, bool)
- func (c *Context) ManifestPath() string
- func (c *Context) Migrate(ctx context.Context, p MigrateParams) error
- func (c *Context) ModulePrefix() string
- func (c *Context) Network() gocmd.Runner
- func (c *Context) PlanAdoption(p PlanAdoptionParams) ([]modparser.Spec, error)
- func (c *Context) Prober() *resolve.Prober
- func (c *Context) Processes() gocmd.ProcessRunner
- func (c *Context) Record(ctx context.Context) error
- func (c *Context) RecreateCommand() string
- func (c *Context) ReleaseSource() selfupdate.Source
- func (c *Context) Resolver() *resolve.Resolver
- func (c *Context) RestoreToRoot(ctx context.Context, p RestoreToRootParams) error
- func (c *Context) RootGoMod() string
- func (c *Context) Runner() gocmd.Runner
- func (c *Context) Sync(ctx context.Context, p SyncParams) error
- func (c *Context) Tools() *tools.GoManager
- func (c *Context) ToolsDir() string
- func (c *Context) ToolsOnDisk() ([]modparser.Spec, error)
- func (c *Context) Tracer(ctx context.Context) tracer.Tracer
- func (c *Context) Unmanaged(specs []modparser.Spec) []modparser.Spec
- func (c *Context) WithLock(ctx context.Context, fn func(context.Context) error) error
- type Doctor
- type Exec
- type Info
- type Init
- type Install
- type InstallParams
- type List
- type Migrate
- type MigrateParams
- type Piped
- type PlanAdoptionParams
- type Purge
- type Remove
- type Rename
- type RestoreToRootParams
- type SelfUpdate
- type Spec
- type Step
- type Sync
- type SyncParams
- type Uninstall
- type Upgrade
- type Version
Constants ¶
const ( NameInit = "init" FlagStrategy = "strategy" FlagDir = "dir" FlagGo = "go" FlagPrefix = "prefix" FlagNoMigrate = "no-migrate" FlagDryRun = "dry-run" )
The init command's name, flags and help text.
const ( NameInstall = "install" FlagForce = "force" FlagOffline = "offline" )
The install command's name, flags and help text.
const ( NamePurge = "purge" FlagRestore = "restore" )
The purge command's name, flags and help text.
const ( NameSelfUpdate = "self-update" // FlagYes answers the confirmation before installing. FlagYes = "yes" )
The self-update command's name and help text.
const ( NameSync = "sync" FlagJobs = "jobs" )
The sync command's name, flags and help text.
const ( NameVersion = "version" FlagFormat = "format" FlagJSON = "json" FlagText = "text" // ProductName is what the version output calls the program, whatever the // binary is named; the root's --version prints it too. ProductName = "gotools" )
The version command's name, flags and help text. The --format, --json and --text flags are shared by every command that can print JSON, so they are declared here once, with the first command that uses them.
const HelpTemplate = helpTemplate
HelpTemplate is the help layout every command shares, for a command that urfave/cli declares itself and that needs the same look.
const (
NameCheck = "check"
)
The check command's name and help text.
const (
NameConfig = "config"
)
The config command's name, argument and help text.
const (
NameDoctor = "doctor"
)
The doctor command's name and help text.
const (
NameExec = "exec"
)
The exec command's name and help text.
const (
NameInfo = "info"
)
The info command's name, argument and help text.
const (
NameList = "list"
)
The list command's name and help text.
const (
NameMigrate = "migrate"
)
The migrate command's name, arguments and help text.
const (
NameRemove = "remove"
)
The remove command's name, argument and help text.
const (
NameRename = "rename"
)
The rename command's name, argument and help text.
const (
NameUninstall = "uninstall"
)
The uninstall command's name and help text.
const (
NameUpgrade = "upgrade"
)
The upgrade command's name, argument and help text.
Variables ¶
This section is empty.
Functions ¶
func Prose ¶
Prose prepares a description for HelpTemplate: {prog} becomes the program name and each paragraph is wrapped for the terminal.
func RunRoot ¶ added in v0.7.1
RunRoot runs root with args and returns its failure as an *errors.Error. A command's own failure already is one and passes through untouched, keeping its exit code. Anything else comes from urfave/cli itself and has no code of its own, so it is a generic failure (exit 1) that keeps its message.
func UsageError ¶
UsageError turns a parse or validation failure into a usage error, exit 2. Without it urfave/cli prints its own message and the whole help, and a caller sees a plain error that exits 1. It is every command's OnUsageError.
Types ¶
type AdoptParams ¶
type AdoptParams struct {
// Specs are the tools the root go.mod declares (see PlanAdoption).
Specs []modparser.Spec
// DryRun reports what would happen and changes nothing.
DryRun bool
}
AdoptParams says what Adopt takes over from the root go.mod.
type Check ¶
type Check struct {
// contains filtered or unexported fields
}
Check confirms that every tool in the manifest runs.
- Before checks the arguments, requires Go and loads the project, so a bad request stops before any tool is launched.
- Run launches each tool once per probe argument until one exits 0 and reports pass or fail per tool.
type Command ¶
type Command interface {
// Name is what the user types.
Name() string
// Build declares the command to urfave/cli, bound to the Context it works
// on.
Build(c *Context) *urfave.Command
}
Command is one gotools command.
type Commander ¶
type Commander struct {
Commands []Command
// contains filtered or unexported fields
}
func (*Commander) RegisterCommand ¶
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config shows or changes the manifest.
- Run reads the manifest and, depending on how many arguments there are, shows the file, shows one key, or sets one key and writes the file.
It reads and writes the file's own values, never the resolved ones, so an environment override is not written back into the manifest.
type Context ¶
type Context struct {
Stdin io.Reader
Stdout, Stderr io.Writer
Environ []string // NAME=value pairs, like os.Environ
Program string // the name used in messages, e.g. "gotools"
Version string // the gotools version, e.g. "v0.6.7"
WorkDir string // where the command was started
Go gocmd.Runner
// Process runs a tool with the caller's streams; the real program when nil.
Process gocmd.ProcessRunner
// Interactive reports that the user can be asked a question: stdin is a
// terminal.
Interactive bool
// Completing reports that the shell is asking for completions: the commands
// answer with words and run nothing else.
Completing bool
// ReadOnly makes Load change nothing on disk: it leaves the temp files a
// crashed write left behind. A command that promises to write nothing sets it.
ReadOnly bool
// Exe locates the running executable; os.Executable when nil.
Exe func() (string, error)
// Releases is where self-update looks for releases; the project's GitHub
// releases when its APIURL is empty.
Releases selfupdate.Source
Root string // the project root
Config config.Config // layout and settings, after every layer
Manifest *manifest.Manifest // the file's values; New() when there is no file
// HasManifest reports whether the manifest file exists.
HasManifest bool
// Warnings are problems found while loading that did not stop it.
Warnings []string
Locker lock.Locker
ModFiles modparser.Parser
// Fingerprint records the state a sync reconciled.
Fingerprint fingerprint.Fingerprinter
}
Context is what a command works on. The first group is handed in by the caller; the second is filled by Load. It grows as commands move over.
func (*Context) Adopt ¶
func (c *Context) Adopt(ctx context.Context, p AdoptParams) error
Adopt installs every tool of p.Specs that the manifest does not serve yet, then removes the directives from the root go.mod and tidies it. The root go.mod is not touched until every install succeeded, so a failure leaves the project as it was and a rerun resumes. The caller holds the lock.
func (*Context) Executable ¶
Executable is the file that holds the running program: the real file behind any symlink, which is what an update must replace.
func (*Context) ExecutablePaths ¶
ExecutablePaths are the path the program was started through and the resolved file behind it, in that order. They differ when it was started through a symlink; an uninstall removes both.
func (*Context) Flush ¶
Flush writes the manifest. An empty module prefix is first resolved from the root go.mod, then the tools directory, so the file never stores an empty one. Values that came from the environment are never in the manifest, so they are never written.
func (*Context) Install ¶
func (c *Context) Install(ctx context.Context, p InstallParams) error
Install installs p.Specs under one lock. A name or package the manifest already has is reconciled first: refused, or, when it is the same package under another name, offered as a choice on a terminal. The project must be loaded and prepared (see prepareInstall).
func (*Context) Load ¶
Load finds the project root, reads the manifest, resolves the configuration (default < manifest < environment < opts) and builds the seams that depend on it. A seam the caller already set is kept.
func (*Context) LoadOrDefaults ¶
LoadOrDefaults is Load for a command that must report on a project even when it cannot be loaded: a manifest from a newer gotools, malformed JSON, no strategy, a bad tools directory. It returns why the project is unusable, or "" when it loaded and is valid. When it could not load, the Context holds a stand-in: no manifest, and a layout from the environment, else the defaults.
func (*Context) ManagedAs ¶
ManagedAs is the manifest name already serving t: its own name, or the name the same package has under another one. A tool that is already managed gets no second name.
func (*Context) ManifestPath ¶
ManifestPath is where the project's manifest lives.
func (*Context) Migrate ¶
func (c *Context) Migrate(ctx context.Context, p MigrateParams) error
Migrate moves the tools directory to p.To. The project must be loaded and the caller holds the project lock.
The old layout is moved aside first and put back if anything fails, so a failure midway loses nothing: not the old layout, not its go.sum files and not an unrelated file kept in the directory. Only gotools' own files are touched.
func (*Context) ModulePrefix ¶
ModulePrefix is the configured module path prefix, else the module path of the root go.mod, else "".
func (*Context) Network ¶
Network is Runner bounded by the operation timeout, for network-bound calls such as go get and mod tidy.
func (*Context) PlanAdoption ¶
func (c *Context) PlanAdoption(p PlanAdoptionParams) ([]modparser.Spec, error)
PlanAdoption lists the tools the root go.mod declares. It refuses when two of them would share one manifest name, because the second would look "already managed", be skipped, and then lose its directive when go.mod is stripped.
func (*Context) Prober ¶
Prober asks go whether the project's tools run. It uses the runner without a timeout: a probe never touches the network unless the tool is missing, and offline mode turns the proxy off.
func (*Context) Processes ¶
func (c *Context) Processes() gocmd.ProcessRunner
Processes runs a tool with the caller's own streams: the injected runner, else the real program.
func (*Context) Record ¶
Record writes the sync fingerprint of the manifest as it stands, so the next sync can take its fast path. Callers record it only when disk and manifest agree.
func (*Context) RecreateCommand ¶
RecreateCommand is the init command that recreates this project's configuration. The tools come back through the go.mod restore; the strategy, directory, Go version and module prefix live only in the manifest.
func (*Context) ReleaseSource ¶
func (c *Context) ReleaseSource() selfupdate.Source
ReleaseSource is where releases come from: the injected Releases, else GitHub.
func (*Context) RestoreToRoot ¶
func (c *Context) RestoreToRoot(ctx context.Context, p RestoreToRootParams) error
RestoreToRoot is the reverse of adoption: it adds every managed tool back to the root go.mod at its pinned version.
The root go.mod and go.sum are snapshotted and put back on any failure, so a failure on tool N of M never leaves go.mod half edited. Package paths come from the committed manifest and reach `go get` as arguments, so each is checked first: one that looks like a flag never gets there.
func (*Context) Runner ¶
Runner is the go runner commands use: the injected one, else the real go binary with GOWORK=off, echoing each call to the error stream when verbose is on.
func (*Context) Sync ¶
func (c *Context) Sync(ctx context.Context, p SyncParams) error
Sync reconciles the tools directory with the manifest. The project must be loaded. Offline takes only the fingerprint fast path and no lock: it cannot write, so concurrent CI jobs sharing a workspace never contend.
func (*Context) Tools ¶
Tools is the tool manager for this project. Build it per command: the go runners and the streams are the command's.
func (*Context) ToolsOnDisk ¶
ToolsOnDisk lists the tools the tools directory declares, for the project's strategy, with the manifest's name for a package it already knows. A modfile that cannot be read, or whose name is unsafe, is skipped and reported in the returned error, alongside the entries that did load.
An aliased tool in the unified strategy keeps its manifest name instead of being discovered again under the name inferred from its binary, and a stray tools/go.mod in the split strategy is not a tool named "go".
func (*Context) Tracer ¶
Tracer is the tracer the settings ask for. With tracing off it is a Nop and costs nothing, not even a go call. Otherwise records are appended to the project's trace log and echoed to stderr, or to stdout when the setting says so; a record that cannot be written is reported on stderr and never fails the command.
type Doctor ¶
type Doctor struct {
// contains filtered or unexported fields
}
Doctor reports on the environment.
- Run loads the project read-only, or a stand-in when it cannot be loaded, so a full report can still be rendered, and hands the facts to doctor.Doctor, which only looks.
A problem is a row in the report and never a failure: the exit code is 0 whatever it finds, and a bad flag is the only way to a non-zero one.
type Exec ¶
type Exec struct {
// contains filtered or unexported fields
}
Exec runs a managed tool.
- Before checks the request, requires Go and loads the project.
- Run has go build the tool (`go tool -n` prints the binary's path), then runs that binary itself, in the directory exec was started from, with gotools' own stdin, stdout and stderr. The tool's exit code is the exit code.
The tool runs where exec was invoked, in every strategy, so `./...`, relative paths and config lookups resolve where the user is. Building in the strategy directory and running the binary elsewhere also leaves the environment alone (no GOWORK=off reaches the tool) and makes the exit status the tool's own.
Everything after the tool's name goes to the tool as it is: exec has no flags of its own.
type Info ¶
type Info struct {
// contains filtered or unexported fields
}
Info shows one tool as text or JSON.
The modfile on disk is authoritative and the manifest fills in what the modfile lacks, so text and JSON agree, and a tool that is on disk but not in the manifest is still shown.
type Init ¶
type Init struct {
// contains filtered or unexported fields
}
Init bootstraps the project: it writes the manifest, adopts the tools the root go.mod already declares, then syncs.
- Before checks the request, requires Go, loads the project with the flags applied, and plans the adoption, so a bad request stops before anything is written. The flags validate their own values.
- Action writes the manifest and installs the adopted tools under the lock, or only reports what it would do with --dry-run.
- After syncs, so the tools directory matches the manifest.
One Init serves one run: it keeps what Before learned for Run and After. Build works on a copy, so building the command twice (for its help, or to ask its name) never shares state.
type Install ¶
type Install struct {
// contains filtered or unexported fields
}
Install adds a tool, or changes its version. With no command named at all, gotools hands it the data piped into stdin (see Pipe).
- Before checks the arguments, requires Go, loads the project and refuses offline mode, so a bad request stops before anything is touched.
- Action installs under one lock. Each install is atomic: a failure stops the run and leaves the earlier ones in place.
One Install serves one run: it keeps what Before learned for Run. Build works on a copy, so building the command twice never shares state.
type InstallParams ¶
type InstallParams struct {
// Specs are the tools to install, one after the other. Each is atomic: a
// failure stops the run and leaves the earlier ones in place.
Specs []tools.AddOptions
// Force overwrites a tool that is already managed under the same name.
Force bool
}
InstallParams says what Install installs and how.
type List ¶
type List struct {
// contains filtered or unexported fields
}
List shows the managed tools as a table or JSON.
Text shows what is on disk, since that is what exec runs, and falls back to the manifest when nothing is on disk yet. JSON always lists the manifest's tools, in the manifest's order. Neither needs Go.
type Migrate ¶
type Migrate struct {
// contains filtered or unexported fields
}
Migrate moves the tools directory to another isolation strategy, keeping every tool at its pinned version.
- Before checks the arguments, requires Go and loads the project.
- Run migrates under the project lock; Context.Migrate does the work, and sync calls it too for an automatic migration.
One Migrate serves one run. Build works on a copy, so building the command twice never shares state.
type MigrateParams ¶
type MigrateParams struct {
// To is the strategy to move to.
To strategy.Strategy
DryRun bool
// PersistStrategy writes To to the manifest. An automatic migration (sync)
// leaves it off: its target is the resolved strategy, which may come from
// the environment, and that must not be written to a committed file.
PersistStrategy bool
}
MigrateParams says how Migrate moves the tools directory.
type Piped ¶
type Piped interface {
Command
// Pipe handles the data read from stdin.
Pipe(ctx context.Context, c *Context, data []byte) error
}
Piped is implemented by the one command that handles data piped to gotools when no command is named, as in `echo pkg@v | gotools`.
type PlanAdoptionParams ¶
type PlanAdoptionParams struct {
// NoMigrate plans nothing: the root go.mod is to be left untouched.
NoMigrate bool
}
PlanAdoptionParams says how PlanAdoption reads the root go.mod.
type Purge ¶
type Purge struct {
// contains filtered or unexported fields
}
Purge deletes the tools directory and the manifest, optionally giving the tools back to the root go.mod first.
- Before checks the arguments and loads the project. A project with no manifest is not a gotools project, so nothing in it is ours to delete.
- Run restores first when asked (a failed restore leaves gotools intact), asks for the word YES before it takes the lock, then deletes under the lock. The manifest goes last: if the directory cannot be removed, it still says what is there.
type Remove ¶
type Remove struct {
// contains filtered or unexported fields
}
Remove deletes tools from the tools directory and the manifest.
- Before checks every name, requires Go and loads the project, so a bad name stops the run before the first tool is touched.
- Run removes each tool under the project lock. A tool that is neither on disk nor in the manifest is reported once the others have been removed.
One Remove serves one run: it keeps what Before read. Build works on a copy, so building the command twice never shares state.
type Rename ¶
type Rename struct {
// contains filtered or unexported fields
}
Rename gives a managed tool another name.
- Before checks both names, requires Go and loads the project.
- Run renames under the project lock: the tool manager moves the files and the manifest entry together and undoes every step if a later one fails.
type RestoreToRootParams ¶
type RestoreToRootParams struct {
// DryRun reports what would be added and runs nothing.
DryRun bool
}
RestoreToRootParams are the options of RestoreToRoot.
type SelfUpdate ¶
type SelfUpdate struct {
// contains filtered or unexported fields
}
SelfUpdate replaces the running binary with the newest release.
- Run asks for the latest release and stops when the running version is already that or newer. Otherwise it asks for confirmation (--yes answers it; no answer at all is a no) and hands the release to selfupdate.Updater, which downloads, verifies and swaps it in.
It needs no project and no Go, and a manifest that cannot be read does not stop it: only the environment can switch offline mode on here.
func NewSelfUpdate ¶
func NewSelfUpdate() *SelfUpdate
NewSelfUpdate returns the self-update command.
type Spec ¶
type Spec struct {
Name string
Aliases []string
Usage string // one-line summary, shown in the overview
ArgsUsage string // positional arguments in the usage line, e.g. "[name] <pkg>"
// Description is the prose of the help; {prog} stands for the program
// name. Write each paragraph as one line: help wraps it. A paragraph with
// an indented line is kept as written.
Description string
// Examples are invocations without the program name.
Examples []string
Flags []urfave.Flag
Args []urfave.Argument
// SkipFlagParsing hands every argument to the command untouched, for a
// command whose arguments belong to another program.
SkipFlagParsing bool
// Before checks the request and loads the project, so a bad request stops
// before anything is touched. Run does the work. After finishes up, and
// runs only when Run succeeded: urfave/cli's own After runs even when the
// action failed, which would sync after a failed adoption. Any may be nil.
Before, Run, After Step
// Complete offers the values the shell may complete an argument to. Nil
// leaves urfave/cli's default: the subcommands and the flags.
Complete urfave.ShellCompleteFunc
}
Spec is everything a command declares: its names, prose, examples, flags and positional arguments, and its behavior as three steps. Build turns it into the urfave/cli command, so a command is written as a Spec and nothing else needs to know how urfave/cli is wired.
type Sync ¶
type Sync struct {
// contains filtered or unexported fields
}
Sync reconciles the tools directory with the manifest.
- Before checks the request, requires Go and loads the project.
- Run syncs: Context.Sync does the work, and init calls it too.
One Sync serves one run: it keeps what Before read from the flags. Build works on a copy, so building the command twice never shares state.
type SyncParams ¶
type SyncParams struct {
DryRun bool
// Offline takes only the fingerprint fast path. The offline setting does
// the same.
Offline bool
// Jobs is how many tools reinstall at once; 0 leaves it to the jobs
// setting.
Jobs int
// Unlocked syncs without taking the project lock: the caller holds it, or
// this is a dry run that must not create the lock file in a tools
// directory that does not exist yet.
Unlocked bool
}
SyncParams says how Sync runs.
type Uninstall ¶
type Uninstall struct {
// contains filtered or unexported fields
}
Uninstall deletes the gotools executable.
- Run names what it would delete, asks for confirmation on stdin, then removes the running binary and the symlink it was started through, so no dangling link is left behind. No answer at all, as in a script, is a no.
Projects keep their manifest and tools directory; only the program goes.
type Upgrade ¶
type Upgrade struct {
// contains filtered or unexported fields
}
Upgrade moves tools to @latest.
- Before checks the names, requires Go, loads the project and refuses offline mode, since @latest comes from the proxy.
- Run upgrades each tool under the project lock. Each is installed and recorded on its own, so a failure on tool N leaves 1..N-1 upgraded and N untouched.