updater

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 13 Imported by: 0

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:

  1. Query the GitHub API for the latest release.
  2. Compare it against the currently running binary's version.
  3. Download install.sh pinned to the target tag.
  4. 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

func StripLeadingV(v string) string

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:

  1. Windows is unconditionally MethodWindows because install.sh refuses it.
  2. 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.
  3. 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

func New(c Config) *Updater

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

func (u *Updater) Check(ctx context.Context, currentVersion string) (Status, error)

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

func (u *Updater) Latest(ctx context.Context) (Release, error)

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

func (u *Updater) RunInstallScript(ctx context.Context, version, installDir string) error

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.

Jump to

Keyboard shortcuts

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