Documentation
¶
Overview ¶
Package toolchain owns a developer tool chain's lifecycle, projected from one list of types.ToolEntry values (a tool manifest the consumer loads and checks with types.ValidateToolEntries; the manifest's schema lives in core/types so a foundation-tier manifest can hold it). Its files:
- tool.go / registry.go — Registry, a lookup of lightweight Tool values (binary + optional command prefix) for RequireTool / prefix expansion (e.g. `uv run ruff`). The composition root builds one from the manifest's tools via DefsFromTools + NewRegistryFromDefs and injects it; there is no package-level registry.
- specs.go — projects the manifest's lifecycle tools (those carrying a `category`) into heavy types.ToolDef specs, resolving the declarative version-parse strategy and per-platform install kinds.
- catalog.go — Catalog, the lifecycle counterpart to the registry: the composition root builds one from the manifest's tools and threads it into the detect/install operations (Prerequisites/Required/Optional).
- detect.go — Detector checks tools for presence + minimum version through an injected CommandRunner (so detection is testable and host-independent).
- install.go / path.go — Installer resolves and runs the platform-appropriate install method, manages PATH, and orchestrates the setup-tool flow (Installer.Provision) and the tool-install flow (Installer.InstallTools: the CI set, named tools, or the optional tools).
- download.go — the pinned download kinds (github-release, url): fetch the host's os-arch artifact through the Downloader seam, refuse it unless its SHA-256 matches the manifest, and atomically place the (extracted) binary in GOBIN.
- pins.go — Installer.CheckPins compares each go-installed CI tool on PATH with the build the manifest pins.
The two tool representations (lightweight Tool, heavy types.ToolDef) coexist, both projected from the same manifest list but serving different consumers.
Index ¶
- func AddToPathIfMissing(dir string, log interfaces.Logger)
- func ClassifyWingetExit(exitCode int) (string, bool)
- func PlatformKey() string
- func ProjectLifecycleTools(tools []types.ToolEntry) ([]types.ToolDef, error)
- func ProjectToolsByCategory(tools []types.ToolEntry, cat types.ToolCategory) ([]types.ToolDef, error)
- func RequireTool(runner interfaces.CommandRunner, t interfaces.ToolSpec) error
- func SummarizeMissing(prereqErr error, results []types.ToolCheckResult) error
- type Catalog
- func (c *Catalog) All() []types.ToolDef
- func (c *Catalog) CI() []types.ToolDef
- func (c *Catalog) Find(binary string) (types.ToolDef, bool)
- func (c *Catalog) Installable() []types.ToolDef
- func (c *Catalog) Optional() []types.ToolDef
- func (c *Catalog) Prerequisites() []types.ToolDef
- func (c *Catalog) Required() []types.ToolDef
- type Def
- type Detector
- func (d *Detector) Check(ctx context.Context, root string, tool *types.ToolDef) types.ToolCheckResult
- func (d *Detector) CheckPrerequisites(ctx context.Context, root string, prereqs []types.ToolDef) error
- func (d *Detector) ListOptional(ctx context.Context, root string, optional []types.ToolDef)
- func (d *Detector) Scan(ctx context.Context, root string, tools []types.ToolDef) []types.ToolCheckResult
- type Downloader
- type Installer
- func (i *Installer) CheckPins(ctx context.Context, root string, tools []types.ToolDef) types.StepResults
- func (i *Installer) Install(ctx context.Context, root string, tool *types.ToolDef) error
- func (i *Installer) InstallSet(ctx context.Context, root string, optional []types.ToolDef, ...) error
- func (i *Installer) InstallTools(ctx context.Context, root string, cat *Catalog, opts types.InstallToolsOptions) error
- func (i *Installer) InstalledVersion(ctx context.Context, root string, tool *types.ToolDef) string
- func (i *Installer) Provision(ctx context.Context, root string, cat *Catalog, autoYes, checkOnly bool) error
- type Option
- type PathResolver
- type Registry
- type Tool
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddToPathIfMissing ¶
func AddToPathIfMissing(dir string, log interfaces.Logger)
AddToPathIfMissing prepends dir to the process PATH when it is not already present (case-insensitive). Idempotent: a dir already on PATH is a no-op.
func ClassifyWingetExit ¶
ClassifyWingetExit reports whether an exit code is a success-like "already installed" condition, with its description.
func PlatformKey ¶
func PlatformKey() string
PlatformKey returns the platform key used to select install methods: "darwin", "windows", or "linux-apt"/"linux-dnf"/"linux-pacman".
func ProjectLifecycleTools ¶
ProjectLifecycleTools projects the lifecycle tools (those carrying a category) of the manifest's tools section into types.ToolDef values. Registry-only entries (no category) are skipped — they exist solely for RequireTool via DefsFromTools.
func ProjectToolsByCategory ¶
func ProjectToolsByCategory( tools []types.ToolEntry, cat types.ToolCategory, ) ([]types.ToolDef, error)
ProjectToolsByCategory returns the lifecycle tools of one category from the manifest's tools section, in manifest order. Install ordering matters (e.g. Node before pnpm), so the slice preserves the manifest sequence.
func RequireTool ¶
func RequireTool(runner interfaces.CommandRunner, t interfaces.ToolSpec) error
RequireTool checks that the tool is available on PATH via the provided runner.
func SummarizeMissing ¶
func SummarizeMissing(prereqErr error, results []types.ToolCheckResult) error
SummarizeMissing consolidates a prerequisite error and a set of scan results into a single CodeUnavailable error listing every unmet dependency, or nil when nothing is missing.
Types ¶
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the projected, in-memory set of lifecycle tool specs, grouped by category. It is the lifecycle counterpart to the Registry: the composition root builds one Catalog from the manifest's tools section and threads it into the detect/install operations, so those operations take their tool data as input rather than reaching for a package global. Registry-only manifest entries (no category) are not part of the catalog — they exist solely for RequireTool via DefsFromTools.
func NewCatalog ¶
NewCatalog projects the lifecycle tools of the manifest's tools section into a Catalog, split by category and preserving manifest order within each (install ordering matters, e.g. Node before pnpm). A manifest types.ValidateToolEntries rejects (a duplicate binary, an unknown install kind, an unparseable min_version, …) is a CodeInvalidInput error.
func (*Catalog) All ¶
All returns every lifecycle spec in setup order: prerequisites, then required, then optional.
func (*Catalog) CI ¶
CI returns the installable tools the manifest marks `ci: true`, in manifest order within each category — the set a CI install provisions.
func (*Catalog) Find ¶
Find returns the lifecycle spec for a binary name, searching all categories. The boolean is false for registry-only or unknown binaries.
func (*Catalog) Installable ¶
Installable returns the tools an install-by-name command can target: the required toolchain, then the optional tools (prerequisites are never installed).
func (*Catalog) Prerequisites ¶
Prerequisites returns the tools that must pre-exist (checked, never installed).
type Def ¶
type Def struct {
// Name is the human-readable display name (e.g. "golangci-lint").
Name string
// Binary is the executable name looked up on PATH.
Binary string
// InstallHint is shown when the tool is missing from PATH.
InstallHint string
// Prefix is an optional command prefix (e.g. ["uv", "run"] for Python tools).
Prefix []string
}
Def describes a tool for registry construction, consumed by NewRegistryFromDefs and produced from the manifest's tools by DefsFromTools.
func DefsFromTools ¶
DefsFromTools projects the manifest's tools into registry Defs: each tool's name, binary, install hint and command prefix.
type Detector ¶
type Detector struct {
// contains filtered or unexported fields
}
Detector checks tools for presence and minimum version. Existence and version lookups route through a CommandRunner so tests can inject a mock. Detection is platform-independent — only installation cares about the host platform.
func NewDetector ¶
func NewDetector(runner interfaces.CommandRunner, log interfaces.Logger) *Detector
NewDetector returns a Detector backed by the given runner and logger.
func (*Detector) Check ¶
func (d *Detector) Check( ctx context.Context, root string, tool *types.ToolDef, ) types.ToolCheckResult
Check reports whether one tool is installed and meets its minimum version. A tool with no MinVersion (or no VersionCmd) is OK as long as its binary is on PATH. Version-check failures are reported as Outdated (found but unusable), while an unparseable version is treated as OK (best-effort, not a hard fail).
func (*Detector) CheckPrerequisites ¶
func (d *Detector) CheckPrerequisites( ctx context.Context, root string, prereqs []types.ToolDef, ) error
CheckPrerequisites checks the prerequisite tools, logging each, and returns a consolidated CodeUnavailable error naming every missing or outdated one (with its fallback URL). Returns nil when all prerequisites are satisfied.
func (*Detector) ListOptional ¶
ListOptional logs every optional tool with its install status and description, for a tool listing.
type Downloader ¶
type Downloader interface {
// Fetch opens the artifact at url for reading; the caller closes the body.
Fetch(ctx context.Context, url string) (io.ReadCloser, error)
}
Downloader fetches a remote artifact — the network seam of the pinned download install kinds (github-release, url). The Installer verifies whatever it returns against the manifest's SHA-256 before anything is placed.
SDK seam — wraps net/http; unit tests inject a generated mock.
type Installer ¶
type Installer struct {
// contains filtered or unexported fields
}
Installer installs tools using platform-appropriate methods, routing every command through a CommandRunner so the exact invocation is testable and a forced platform exercises platform-specific branches without a real install. The platform key is injected (not detected) so callers — and tests — control it.
func NewInstaller ¶
func NewInstaller( runner interfaces.CommandRunner, log interfaces.Logger, platform string, prompter interfaces.Prompter, opts ...Option, ) *Installer
NewInstaller returns an Installer for the given platform key (see PlatformKey). The host architecture defaults to runtime.GOARCH (used to pick a download's os-arch artifact and for the Windows-ARM64 cross-compile rewrite) and can be overridden via WithArch; the artifact downloader defaults to HTTPS and can be overridden via WithDownloader; the PATH lookup defaults to os/exec.LookPath and can be overridden via WithPathResolver. The prompter confirms an install when AutoYes is off; it is required — a nil prompter fails such an install with a coded error.
func (*Installer) CheckPins ¶
func (i *Installer) CheckPins( ctx context.Context, root string, tools []types.ToolDef, ) types.StepResults
CheckPins reports, one step per CI tool, whether the binary on PATH is the build the manifest pins. Only tools installed with `go install <package>@<version>` on this platform are checked: their binaries embed the package path and module version, which `go version -m` reads, so no per-tool version command is needed. A drift fails with the reinstall command; a tool that is not on PATH, or whose binary carries no build info, is skipped (the lint/format steps that need it report a missing tool themselves).
This is the guard for formatter drift: gofumpt and golines at another version format the same file differently, so a local run that "passes" fails CI's format check.
func (*Installer) Install ¶
Install installs one tool using the method resolved for the Installer's platform, returning a CodeUnavailable error when no method applies. A pinned download method is fetched, verified against its SHA-256, and placed in the install dir (GOBIN) without running any command; it fails, leaving nothing installed, on a mismatch or when the host's os-arch has no pinned checksum.
func (*Installer) InstallSet ¶
func (i *Installer) InstallSet( ctx context.Context, root string, optional []types.ToolDef, opts types.InstallToolsOptions, ) error
InstallSet installs a pool of tools (the optional, installable, or CI set InstallTools picked): select targets (all, or the named subset), skip already-installed ones (unless Force), prompt (unless AutoYes), install each, and report failures.
func (*Installer) InstallTools ¶
func (i *Installer) InstallTools( ctx context.Context, root string, cat *Catalog, opts types.InstallToolsOptions, ) error
InstallTools is the tool-install entry point. It picks the pool the request targets — exactly the ci-marked tools (opts.CI), the named tools from every installable category, or, with neither, the optional tools — and runs the InstallSet flow over it. --ci with names is rejected: the CI set is all-or-nothing. --ci implies Force: CI must run the pinned binaries, never a runner's preinstalled copy of the same name at another version.
func (*Installer) InstalledVersion ¶ added in v0.0.19
func (i *Installer) InstalledVersion( ctx context.Context, root string, tool *types.ToolDef, ) string
InstalledVersion identifies the build of tool on PATH, for keying cached results by the tool that produced them: a Go binary's main package and module version from its build info (`go version -m`), else the trimmed output of the tool's VersionCmd. It is "" when the binary is not on PATH or neither source answers. It reads the binary, never the catalog's pin, so a reinstall at another version changes it.
func (*Installer) Provision ¶
func (i *Installer) Provision( ctx context.Context, root string, cat *Catalog, autoYes, checkOnly bool, ) error
Provision runs the required-tool flow for setup: pin the local Go toolchain, ensure the go-install bin dir is on PATH, check prerequisites, scan the required toolchain, and — unless checkOnly — install the missing ones that have an applicable method, verifying each afterwards. A required tool with no install method for this platform (e.g. gcc off Windows) is reported, not force-installed.
type Option ¶
type Option func(*Installer)
Option configures an Installer at construction: its dependencies are injected, not detected in a constructor.
func WithArch ¶
WithArch overrides the host architecture used for the Windows-ARM64 cross-compile rewrite. The default is runtime.GOARCH; injecting it makes the arch-specific rewrite deterministically testable without a matching host.
func WithDownloader ¶
func WithDownloader(d Downloader) Option
WithDownloader overrides the artifact Downloader (default: an HTTPS GET that fails on any non-200 status), so the verified install path is testable without the network.
func WithForce ¶
WithForce makes Provision reinstall required tools even when already present at a satisfying version — the a forced setup rebuild path after a Go bump.
func WithPathResolver ¶
func WithPathResolver(p PathResolver) Option
WithPathResolver overrides how CheckPins finds a tool's binary on PATH (default: os/exec.LookPath), so the pin check is testable without a binary on PATH.
func WithReinstallHint ¶
WithReinstallHint overrides the command a pin drift tells the operator to run, given the tool's binary and its pinned package@version (default: `go install <package>@<version>`), so a front-end can point at its own install command.
type PathResolver ¶
type PathResolver interface {
// LookPath returns the path of the named executable, or an error when it is not on
// PATH.
LookPath(file string) (string, error)
}
PathResolver resolves a tool's binary name to the executable found on PATH — the seam CheckPins reads the installed build through.
SDK seam — wraps os/exec.LookPath; unit tests inject a generated mock.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds types.Tool instances keyed by binary name.
func NewRegistryFromDefs ¶
NewRegistryFromDefs builds a Registry holding one types.Tool per Def. A later Def with the same binary replaces an earlier one.
type Tool ¶
type Tool struct {
// contains filtered or unexported fields
}
Tool is the lightweight representation of an external binary (e.g. buf, ruff, go).
func NewTool ¶
NewTool creates a Tool with the given name, binary, install hint, and optional command prefix (e.g. ["uv", "run"] for Python tools invoked via uv).
func (*Tool) Cmd ¶
Cmd builds the full command slice with prefix expansion. For tools with a prefix (e.g. "uv run ruff"), Cmd("check", ".") returns ("uv", ["run", "ruff", "check", "."]). For tools without a prefix, returns (binary, args).
func (*Tool) InstallHint ¶
InstallHint returns installation instructions shown when the tool is missing.