Documentation
¶
Overview ¶
Package cliinstall carries the fleet's compiled-in catalog of installable CLIs (cli-install#req:catalog-compiled-in): a stable id per CLI, its release identity in the github.com/strongo/cli-helpers/selfupdate Config shape, its Homebrew cask coordinates, and the host -> target relevance texts a fleet CLI shows when it lists or explains its siblings.
Identity lives here, once ¶
Every per-CLI identity constructor — repository, tag prefix, managers, supported platforms, asset and checksum naming, version-probe arguments — lives in this package as an Entry, never in selfupdate itself (cli-install#req:catalog-identity-single-source). A host builds its own self-update Config from its own Entry.Config, so its self-update and every other host's "install <that cli>" resolve releases identically. This couples a CLI's release naming to a cli-helpers release: a CLI that changes its GoReleaser archive or checksum naming must first update its catalog entry here.
Built on the catalog ¶
cliinstall carries the catalog's data and validates it against recorded snapshots (see the gen subpackage and TestCatalog*), locates and probes installed copies (Probe), plans a destination and install method (Install's own planning, unexported), and performs a direct or Homebrew install. It has no command framework or terminal dependency (cli-install#req:core-framework-neutral); the optional text/JSON writers and Cobra adapter are a later piece of the cli-install Feature, built on top of this package.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type BatchResult ¶ added in v0.17.0
BatchResult is the outcome of one batch Install call: the running host's own catalog id and one Result per de-duplicated named target, in the order names first named them (cli-install#req:multi-target-batch).
func Install ¶ added in v0.17.0
Install processes every name in names against the running host, in the order given, de-duplicated (cli-install#req:multi-target-batch). It looks up each name in the compiled catalog, probes every valid target's current status, plans an install method and destination for whichever are not already installed, asks at most one confirmation covering every target that would actually be installed, and — once confirmed — installs each one independently: one earlier failure never stops a later target. The returned error is non-nil only for a batch-level refusal before any target-specific outcome was decided (the confirmation gate's own non-interactive refusal); every other outcome, including every per-target failure, is reported in BatchResult.Results.
func (BatchResult) Failed ¶ added in v0.17.0
func (b BatchResult) Failed() bool
Failed reports whether at least one Result in b has OutcomeFailed (cli-install#req:multi-target-batch: "the command fails when at least one target failed"). A host's Cobra adapter uses this, together with each failed Result's typed Failure, to decide its own exit code.
type Entry ¶
type Entry struct {
// ID is the catalog id, equal to the binary name
// (cli-install#req:host-identity-from-catalog).
ID string
// Homepage is the CLI's canonical homepage URL.
Homepage string
// Description is a one-line description shown in a listing row.
Description string
// Details is a longer description of at most a few short paragraphs,
// shown before a confirmation prompt
// (cli-install#req:details-before-install).
Details string
// Repository is "owner/repo" on GitHub that publishes this CLI's
// releases — see selfupdate.Config.Repository.
Repository string
// TagPrefix selects this binary's releases within Repository when that
// repository publishes more than one product's releases — see
// selfupdate.Config.TagPrefix.
TagPrefix string
// Managers are the package managers that might own this binary's
// install, exactly as this CLI's own self-update declares them — see
// selfupdate.Config.Managers.
Managers []selfupdate.Manager
// SupportedPlatforms restricts install and self-replace to the
// GOOS/GOARCH pairs this CLI's release actually publishes — see
// selfupdate.Config.SupportedPlatforms.
SupportedPlatforms []selfupdate.Platform
// VersionProbeArgs are the arguments run against a newly installed copy
// to confirm its version. A zero value leaves selfupdate.Config's own
// default ({"--version"}) in effect.
VersionProbeArgs []string
// UndeterminedVersions lists the CurrentVersion values that mean "this
// build cannot say its own version". A zero value leaves
// selfupdate.Config's own default ({"dev"}) in effect.
UndeterminedVersions []string
// AssetName names this CLI's release archive for one version/platform
// combination. Nil leaves selfupdate.Config's GoReleaser-shaped default
// in effect, which every catalog entry's archive naming matches.
AssetName func(binary, version, goos, goarch string) string
// ChecksumsName names this CLI's release checksums file for one
// version. Nil leaves selfupdate.Config's GoReleaser-shaped default
// ("<binary>_<version>_checksums.txt") in effect; some CLIs publish a
// single flat "checksums.txt" instead and override this.
ChecksumsName func(binary, version string) string
// CaskToken is the argument to `brew install --cask`, tap-qualified
// (e.g. "sneat-dev/tap/wb"). Empty means this CLI publishes no
// Homebrew cask.
CaskToken string
// CaskOS lists the GOOS values ("darwin", "linux") CaskToken's cask
// supports. Empty when CaskToken is empty.
CaskOS []string
// LegacyVersionSignatures optionally lists bare `--version` output
// patterns that identify an old build of this CLI, for the
// status-probe-order fallback step
// (cli-install#req:status-probe-order). Empty when no such pattern is
// declared for this CLI.
LegacyVersionSignatures []string
}
Entry is one compiled-in catalog record: a fleet CLI's stable identity, its release identity in the Self-Update Library's Config shape, its Homebrew cask coordinates, and the descriptive text a host shows a user before installing it (cli-install#req:catalog-entry-identity).
func ByID ¶
ByID returns the catalog entry for id and whether it was found (cli-install#req:host-identity-from-catalog).
func Entries ¶
func Entries() []Entry
Entries returns every catalog entry, sorted by id, as a defensive copy — mutating the returned slice or its elements' slice/func fields never affects the compiled-in catalog.
func (Entry) Config ¶
func (e Entry) Config(currentVersion string) selfupdate.Config
Config returns e's release identity as a selfupdate.Config for a build currently reporting currentVersion (cli-install#req:catalog-identity- single-source). It reproduces e's fields exactly; a caller that needs something only its own self-update requires (an AfterUpdate hook, for instance, which is a cobracmd.CommandOptions field, not a Config one) adds it outside this method.
type Env ¶ added in v0.16.0
type Env struct {
// PathDirs returns the current PATH's directories, in order. It need
// not exclude relative entries itself — Probe filters those
// defensively — but DefaultEnv's implementation does anyway, since a
// relative entry is never meaningful to report as a directory that
// was searched.
PathDirs func() []string
// HostDir returns the running host CLI's own executable directory
// (cli-install#req:status-locate). Returning a non-nil error is
// treated as "no host directory to search", not a fatal Probe error.
HostDir func() (string, error)
// IsExecutable reports whether path names an executable regular file
// — "not merely a file of that name" (cli-install#req:status-locate).
IsExecutable func(path string) bool
// EvalSymlinks resolves path's symlinks for classification
// (cli-install#req:status-locate). May be left nil, in which case
// classification uses only the unresolved path.
EvalSymlinks func(path string) (string, error)
// Run executes path with args directly, without a shell, with empty
// stdin, "NO_COLOR=1" set, and returns the combined stdout+stderr —
// so a probed binary's error or usage text is still available for
// Status.Output when a step doesn't recognize the subcommand. Run
// MUST honor ctx's deadline by killing the process when it expires
// (cli-install#req:status-probe-bounded) and MUST NOT itself retry,
// write any file, or make a network request.
Run func(ctx context.Context, path string, args []string) ([]byte, error)
}
Env carries every side-effecting dependency Probe uses: PATH and process execution, filesystem access, and symlink resolution (cli-install#req:no-network-in-tests: "PATH and environment, executable probing... MUST be injectable"). A caller that wants Probe to see the real host passes DefaultEnv(); a test passes a fake, purpose-built Env and never touches a real installed binary or a real PATH.
Every field is required — DefaultEnv sets all of them, and Probe calls them unconditionally, so an Env built by hand must do the same or the missing field's nil func value panics on first use, the same as calling any other nil func.
func DefaultEnv ¶ added in v0.16.0
func DefaultEnv() Env
DefaultEnv returns an Env wired to the real host: the real "PATH" environment variable, the real running executable's directory, real filesystem and symlink checks, and real (network-free, no-shell) process execution. It is the Env a production `install`/`version`-probing command wires in; tests use a purpose-built Env instead.
type InstallEnv ¶ added in v0.17.0
type InstallEnv struct {
Env
// UserHomeDir returns the current user's home directory, used only on
// non-Windows platforms to build the per-user bin directory.
UserHomeDir func() (string, error)
// Getenv reads one environment variable, used for %LOCALAPPDATA% and
// the destination denylist's Windows roots and $GOROOT.
Getenv func(string) string
// MkdirAll creates the per-user bin directory (mode 0755) only when a
// real, non-dry-run install actually needs it
// (cli-install#req:per-user-bin-dir: "created only for a real
// install... when missing").
MkdirAll func(dir string, perm fs.FileMode) error
// RunManaged executes `brew install --cask <token>` as structured
// argv, streaming its own output
// (cli-install#req:homebrew-cask-install). Required whenever a batch
// actually runs a (non-print-only) Homebrew install; nil fails that
// install with KindManagedCommand rather than panicking (see
// executeHomebrewInstall).
//
// This package builds no default implementation itself: doing so would
// mean deciding what to stream brew's output to, and this core layer
// MUST NOT touch the terminal or assume any I/O streams
// (cli-install#req:core-framework-neutral). A caller wires one from its
// own owned streams — e.g. selfupdate/cliui.ManagedCommandRunner(in,
// out, errOut) — exactly as selfupdate's own cobracmd adapter wires
// Options.RunManaged for self-replace.
RunManaged selfupdate.ManagedCommandRunner
}
InstallEnv extends Env with the additional side-effecting dependencies planning and installing need beyond status probing: per-user directory lookup, directory creation, and the managed command runner Homebrew installs run through (cli-install#req:no-network-in-tests: "the managed command runner, the per-user bin directory... MUST be injectable"). Env is embedded so an InstallEnv is usable anywhere an Env is required, e.g. passing opts.Env.Env straight to Probe.
func DefaultInstallEnv ¶ added in v0.17.0
func DefaultInstallEnv() InstallEnv
DefaultInstallEnv returns an InstallEnv wired to the real host: the real DefaultEnv, the real user home directory and environment, and real directory creation. RunManaged is left nil — see its own doc comment — and MUST be set by the caller before a batch that might run a real Homebrew install. It is what a production `install` command starts from; tests use a purpose-built InstallEnv instead.
type Method ¶ added in v0.17.0
type Method int
Method is how a target will be, or was, installed.
const ( // MethodDirect means a verified release asset is downloaded and placed // at a destination path this package chose // (cli-install#req:direct-release-install). MethodDirect Method = iota // MethodHomebrew means `brew install --cask <token>` places the target // (cli-install#req:homebrew-cask-install). MethodHomebrew )
type Options ¶ added in v0.17.0
type Options struct {
// HostID is the running host's own catalog id
// (cli-install#req:host-identity-from-catalog). It MUST be a valid
// catalog id; an absent one is a programming error Install panics on,
// per that REQ's "caught by the host's tests, not a runtime state
// users see."
HostID string
// Dir is the --dir flag's value; empty when it was not given.
Dir string
// Yes skips the confirmation gate (cli-install#req:confirmation-gate),
// matching selfupdate's own --yes convention.
Yes bool
// DryRun walks the full decision path without any write, brew
// invocation, directory creation, or confirmation
// (cli-install#req:install-dry-run).
DryRun bool
// HomebrewPrintOnly reports a Homebrew install's command instead of
// running it (cli-install#req:homebrew-cask-install).
HomebrewPrintOnly bool
// Env carries every side-effecting dependency.
Env InstallEnv
// Confirm asks whether to proceed with every target that would be
// installed, called at most once per batch, only when at least one
// target needs it and Yes is false
// (cli-install#req:confirmation-gate). Its own refusal — no
// interactive terminal and Yes false — is reported by returning a
// *selfupdate.Failure{Kind: selfupdate.KindNonInteractive}, exactly as
// selfupdate.Options.Confirm documents
// (self-update#req:non-interactive-refusal); Install has no
// interactive-terminal opinion of its own; it relies entirely on this
// callback to enforce it.
Confirm func(names []string) (bool, error)
// ConfigureRelease optionally overrides a target's resolved
// selfupdate.Config before it is used to install or resolve that
// target's release — the release-endpoint injection point
// cli-install#req:no-network-in-tests requires. Nil keeps the
// catalog's own defaults (the real GitHub API).
ConfigureRelease func(target Entry, cfg selfupdate.Config) selfupdate.Config
// ProbeOptions tunes status probing; the zero value is
// production-correct (see ProbeOptions).
ProbeOptions ProbeOptions
}
Options configures Install. Every side-effecting dependency is injectable (cli-install#req:no-network-in-tests).
type Outcome ¶ added in v0.17.0
type Outcome int
Outcome classifies one target's batch install result (cli-install#req:multi-target-batch).
const ( // OutcomeInstalled means the target was freshly installed and // post-install verification ran. OutcomeInstalled Outcome = iota // OutcomeAlreadyInstalled means the target's status was already // Installed; nothing was downloaded, reinstalled or replaced // (cli-install#req:already-installed-no-op). OutcomeAlreadyInstalled // OutcomeRedirected means a Homebrew install was configured print-only: // the command was reported, never run // (cli-install#req:homebrew-cask-install). OutcomeRedirected // OutcomeDryRun means --dry-run reported the planned action without // performing it (cli-install#req:install-dry-run). OutcomeDryRun // OutcomeDeclined means the batch confirmation was asked and declined; // nothing was installed and this is not a failure // (cli-install#req:confirmation-gate). OutcomeDeclined // OutcomeFailed means installing (or planning to install) this target // failed with the typed Failure. OutcomeFailed )
type ProbeOptions ¶ added in v0.16.0
type ProbeOptions struct {
// Concurrency is how many targets are located and probed at once.
// Zero defaults to 4.
Concurrency int
// Budget is the per-target time budget covering every probe step.
// Zero defaults to 3 seconds.
Budget time.Duration
}
ProbeOptions tunes Probe's concurrency and per-target time budget. The zero value is production-correct per cli-install#req:status-probe- bounded (a 3 second per-target budget, at least four targets probed concurrently); tests override both fields to stay fast and deterministic without faking Env.Run's own timeout behavior.
type Relevance ¶
type Relevance struct {
// Target is the relevant CLI's catalog id.
Target string
// Text is the relevance text written for this exact host/target pair.
Text string
}
Relevance is one host -> target row of the catalog's relevance matrix (cli-install#req:relevance-matrix): why a user of the host would want the target.
type Result ¶ added in v0.17.0
type Result struct {
// Target is the catalog id this result describes, or — only for
// OutcomeFailed with a KindUnknownTarget Failure — the raw name that
// was not a catalog id.
Target string
// Outcome classifies what happened to Target.
Outcome Outcome
// Method is how Target will be, or was, installed. Meaningful only when
// Outcome is OutcomeInstalled, OutcomeRedirected, OutcomeDryRun, or
// OutcomeFailed after a destination was already planned.
Method Method
// Destination is the full destination file path for a MethodDirect
// plan or install; empty for MethodHomebrew.
Destination string
// CaskArgv is the exact `brew install --cask <token>` argv for a
// MethodHomebrew plan, redirect, or install; nil for MethodDirect.
CaskArgv []string
// Version and Tag are the release that was (or, for a dry run, would
// be) installed. Tag is the exact published tag, which may differ from
// Version by a repository's TagPrefix and/or a leading "v".
Version string
Tag string
// UpdateHint names the command that updates Target, set only for
// OutcomeAlreadyInstalled (cli-install#req:already-installed-no-op).
UpdateHint string
// Status is Target's probed install state: the pre-install probe for
// every outcome except OutcomeInstalled, which carries the
// post-install re-probe (cli-install#req:post-install-verification).
// Zero when Target was never a valid catalog id.
Status Status
// Failure is set exactly when Outcome is OutcomeFailed.
Failure *selfupdate.Failure
// Warnings are human-readable, non-fatal notes: a shell command cache
// hint after a real install, a shadowing notice, a PATH or
// post-install-verification remedy, or Status's own warnings.
Warnings []string
}
Result is one named target's outcome from a batch Install call — the shape task-5's output writers and Cobra adapter consume. Every fact this module's REQs require appears here as a field, in the same shape Status itself uses for probed identity, so a writer can flatten Result directly into cli-install#req:machine-readable-output's JSON document.
type State ¶ added in v0.16.0
type State int
State is a located target's install state (cli-install#req:status-probe-order, cli-install#req:list-relevant).
const ( // NotInstalled means no executable named after the target's id (with // the platform suffix) was found on PATH, in the host directory, or in // dir. NotInstalled State = iota // Installed means a located copy's identity was confirmed by one of // the three status-probe-order steps. Installed // Unrecognized means a copy was located but no probe step confirmed // this target's identity — never trusted or reported as installed // (cli-install#req:unrecognized-copy-not-trusted). Unrecognized )
type Status ¶ added in v0.16.0
type Status struct {
// ID is the catalog id this Status describes.
ID string
// State is this target's install state.
State State
// Path is the primary reported copy: the first PATH match, or — when
// no copy is on PATH — the first copy found in the host directory or
// dir (cli-install#req:status-locate). Empty when State is
// NotInstalled.
Path string
// OnPath reports whether Path itself was found on an absolute PATH
// entry. False means Path was found only in the host directory or
// dir, in which case Warnings carries a not-on-PATH warning.
OnPath bool
// OtherPaths lists every other located copy of this target, in the
// order they were found (cli-install#req:status-locate: "additional
// copies... count in text, full paths in JSON").
OtherPaths []string
// Method classifies Path's install method, checking both Path itself
// and its symlink-resolved form against every manager declared
// anywhere in the compiled catalog, preferring managed
// (cli-install#req:status-locate). Meaningful only when State is
// Installed or Unrecognized.
Method selfupdate.InstallMethod
// Manager identifies the owning package manager when Method is
// selfupdate.Managed; nil otherwise.
Manager *selfupdate.Manager
// Version, Commit, Date and DateSource are read from whichever step
// VersionSource names. Commit and Date are "" when unknown; a raw
// "none" or "unknown" token from a text probe is normalized to ""
// (cli-install#req:status-probe-order). Meaningful only when State is
// Installed.
Version string
Commit string
Date string
DateSource string
// VersionSource names the step that produced Version/Commit/Date/
// DateSource.
VersionSource VersionSource
// Output is the trimmed combined output of the last probe step that
// produced any, kept for display when State is Unrecognized
// (cli-install#req:status-probe-order: "reported with its path and
// the output that was seen").
Output string
// Warnings are human-readable, non-fatal notes: a not-on-PATH warning
// when OnPath is false, or a timeout warning when this target's probe
// budget was exhausted.
Warnings []string
}
Status is one target's located and probed install state — the type task-4's planner and task-5's output writers consume. It is produced entirely offline and read-only by Probe (cli-install#req:list-offline- read-only).
func Probe ¶ added in v0.16.0
Probe locates and identifies every entry in targets — searching absolute PATH entries, the host executable's directory, and dir (empty when no --dir was given) — and returns one Status per target, in targets' own order (cli-install#req:status-locate, cli-install#req:status-probe-order, cli-install#req:status-probe-bounded). It makes no network requests and writes, moves or deletes nothing (cli-install#req:list-offline-read- only); every side-effecting operation goes through env, so tests need exec no real installed binary.
type VersionSource ¶ added in v0.16.0
type VersionSource int
VersionSource identifies which status-probe-order step produced a Status's Version/Commit/Date/DateSource fields. Meaningful only when State is Installed.
const ( // VersionSourceNone means no step produced version information — // State is not Installed. VersionSourceNone VersionSource = iota // VersionSourceJSON means step 1, `version --json`, succeeded. VersionSourceJSON // VersionSourceText means step 2, plain `version` text, succeeded. VersionSourceText // VersionSourceFlag means step 3, `--version` matching a declared // legacy signature, succeeded. VersionSourceFlag )
func (VersionSource) String ¶ added in v0.16.0
func (v VersionSource) String() string
String renders VersionSource as the stable token REQ: machine-readable- output's "version_source" JSON field carries.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Command gen records, under cliinstall/testdata/snapshots/, a snapshot of each catalog CLI's real published release asset list and (where one exists) its real Homebrew cask file.
|
Command gen records, under cliinstall/testdata/snapshots/, a snapshot of each catalog CLI's real published release asset list and (where one exists) its real Homebrew cask file. |