Documentation
¶
Overview ¶
Package updater handles self-upgrades of the meta-cli binary by delegating to the official install.sh script pinned to a specific release tag.
The package deliberately does NOT reimplement any installer logic (platform detection, archive download, checksum verification, archive layout). That logic lives in install.sh at the repository root and is treated as the single source of truth — this package's only job is:
- Query the GitHub API for the latest release.
- Compare it against the currently running binary's version.
- Download install.sh pinned to the target tag.
- Execute it with META_CLI_VERSION and META_CLI_INSTALL_DIR set.
Pinning the install.sh URL to the target tag (not main) is a hard requirement: the upgrade procedure must be reproducible against a specific commit, so a user who upgrades to v0.3.0 gets the install.sh that shipped with v0.3.0, not whatever happens to be on main today.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func StripLeadingV ¶
StripLeadingV removes a single leading 'v' or 'V' so "v0.2.1" and "0.2.1" compare equal. This is the single source of truth for version normalization; CLI layers should call this rather than reimplementing it.
Types ¶
type Config ¶
type Config struct {
Owner string
Repo string
APIBaseURL string
RawBaseURL string
HTTPClient *http.Client
UserAgent string
}
Config configures an Updater. Zero values fall back to production defaults in New, so callers only need to set fields they actually want to override (the CLI's hidden test flags use this to redirect HTTP calls to httptest).
type InstallMethod ¶
type InstallMethod int
InstallMethod classifies how the running meta-cli binary was installed. This is the signal that decides whether `meta update` should re-run install.sh, print a `go install` hint, or refuse (Windows).
const ( // MethodUnknown means classification failed or the binary was built in a // way we don't specifically recognize. Treated as MethodScript by the // update command because install.sh is the lowest-surprise upgrade path // on Linux and macOS. MethodUnknown InstallMethod = iota // MethodScript is a binary produced by goreleaser and installed either // via install.sh or a manual download. These binaries have // BuildInfo.Main.Version == "(devel)" because goreleaser builds from a // local checkout and stamps the version through -ldflags. MethodScript // MethodGoInstall is a binary produced by `go install module@version`. // These binaries have BuildInfo.Main.Version set to a concrete semver. MethodGoInstall // MethodWindows indicates the binary is running on Windows. install.sh // refuses Windows, so `meta update` cannot delegate to it and must tell // the user to download a release manually. MethodWindows )
func DetectInstallMethod ¶
func DetectInstallMethod(execPath string) InstallMethod
DetectInstallMethod classifies how the running binary was installed so the update command can pick the right upgrade path.
Logic:
- Windows is unconditionally MethodWindows because install.sh refuses it.
- If runtime/debug reports a concrete module version on Main (anything other than "" or "(devel)"), the binary was built by `go install module@version`, which stamps the module version into build info. Return MethodGoInstall so the command prints the `go install ...@latest` hint.
- Otherwise return MethodScript. This covers goreleaser-built binaries, which leave Main.Version as "(devel)" because goreleaser builds from a local checkout and injects the version via -ldflags.
execPath is reserved for future heuristics (for example, comparing against $GOBIN to distinguish `go install` more reliably on exotic setups). It is kept in the signature now so the update command doesn't need to change when we extend detection later.
func (InstallMethod) String ¶
func (m InstallMethod) String() string
String returns the lowercase name used in error messages and JSON output.
type Release ¶
type Release struct {
TagName string `json:"tag_name"`
HTMLURL string `json:"html_url"`
PublishedAt string `json:"published_at"`
}
Release mirrors the subset of the GitHub "latest release" response we care about.
type Status ¶
type Status struct {
Current string `json:"current"`
Latest string `json:"latest"`
UpToDate bool `json:"up_to_date"`
Release Release `json:"release"`
}
Status is the result of comparing the currently installed version to the latest published release.
type Updater ¶
type Updater struct {
// contains filtered or unexported fields
}
Updater is the entry point for the self-update flow. All state is immutable after New returns, so a single Updater is safe to share across goroutines.
func New ¶
New returns an Updater with the given configuration. Any zero-valued field is replaced with the production default so callers can pass Config{} for "just do the right thing."
func (*Updater) Check ¶
Check fetches the latest release and compares it against currentVersion.
Versions are compared as strings after stripping a single leading 'v' from both sides. We intentionally avoid a full semver comparator because the only thing that matters for self-update is "is the user running the exact tag GitHub currently marks as latest?" — the install.sh script uses the same "latest-tag" contract, so matching its semantics keeps the two layers consistent. Prereleases and build metadata are handled by whatever the GitHub "latest" endpoint returns, which is the same source of truth install.sh consults.
func (*Updater) Latest ¶
Latest fetches the "latest release" metadata for the configured repository.
It uses the documented GitHub endpoint and the recommended Accept: application/vnd.github+json media type, with a User-Agent header because the GitHub API rejects anonymous requests without one.
func (*Updater) RunInstallScript ¶
RunInstallScript downloads the install.sh that shipped with v{version} and executes it with META_CLI_VERSION and META_CLI_INSTALL_DIR set, inheriting stdin/stdout/stderr so the user sees the script's live progress output.
The URL is deliberately pinned to v{version} (not main): upgrading to v0.3.0 must use the install.sh that shipped with v0.3.0, so the upgrade procedure is reproducible against a specific commit.
The script is written to a private temp file (mode 0700) rather than piped directly into `sh`. This is necessary because RunInstallScript needs to be interruptible via ctx, and `exec.CommandContext` can only cancel a child it spawned itself.