selfupdate

package
v1.31.4 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package selfupdate implements install-method detection, release resolution, download + checksum verification, and atomic executable replacement for the `ingitdb self-update` command.

Index

Constants

View Source
const DevVersion = "dev"

DevVersion is the placeholder reported by binaries built without -ldflags.

Variables

This section is empty.

Functions

func AssetName

func AssetName(version, goos, goarch string) string

AssetName builds the GoReleaser archive name for the given version and target OS/arch. The version's leading "v" is stripped to match GoReleaser's name_template ("ingitdb_{Version}_{os}_{arch}"). Windows uses .zip; all other platforms use .tar.gz.

func CompareVersions

func CompareVersions(a, b string) int

CompareVersions orders two semver-ish version strings, returning -1 if a < b, 0 if they are equal, and +1 if a > b. A leading "v" is ignored. Comparison is by numeric major/minor/patch; a prerelease suffix (after "-") sorts lower than the same release without it, per semver. This is a minimal comparison sufficient for the self-update downgrade guard, not a full semver implementation.

func ManagerName

func ManagerName(m Manager) string

ManagerName returns a human-readable name for a manager, for display in the redirect message.

func ReplaceExecutable

func ReplaceExecutable(targetPath, newBinaryPath string) error

ReplaceExecutable atomically swaps the binary at newBinaryPath into targetPath. The new binary is first staged to a temp file in the same directory as targetPath (same filesystem, so os.Rename is atomic), with executable permissions (0755), then renamed over the target.

On POSIX, os.Rename within one directory atomically replaces the target — the install location is never left with a partial or truncated file: it holds either the original or the complete new binary. On Windows, a running .exe cannot be overwritten, so the existing target is first renamed aside to targetPath+".old" before the new binary is moved into place.

Any staging error before the rename returns the error and leaves targetPath untouched. Underlying os errors are wrapped with %w so callers can classify them (e.g. errors.Is(err, fs.ErrPermission)).

func UpgradeCommand

func UpgradeCommand(m Manager) (string, bool)

UpgradeCommand returns the exact upgrade command a user should run for a package-manager-managed install, and true when m is a recognized manager. For ManagerNone or any unknown manager it returns ("", false).

ingitdb's managed distribution channels are the Homebrew cask and Snap.

func VerifyBinaryVersion

func VerifyBinaryVersion(path, wantVersion string) error

VerifyBinaryVersion runs "<path> version" (the ingitdb version subcommand; the root command has no --version flag) and returns an error unless the command output contains wantVersion. It is kept separate from ReplaceExecutable so the swap and the post-swap sanity check are independently testable.

Types

type CheckResult

type CheckResult struct {
	Current string
	Latest  string
	Verdict Verdict
}

CheckResult captures the comparison between the current build and the latest stable release.

func Compare

func Compare(current, latestTag string) CheckResult

Compare determines the verdict for a current build version against the latest stable release tag. A "dev" current version is Undetermined. Leading "v" prefixes are normalized before comparison.

type Detection

type Detection struct {
	Method  InstallMethod
	Manager Manager
}

Detection is the result of classifying an executable path.

func Classify

func Classify(execPath string) Detection

Classify decides the install method purely from execPath. It is case-insensitive and treats both '/' and '\' as path separators so Windows paths can be tested on any host.

func DetectSelf

func DetectSelf() (Detection, error)

DetectSelf resolves the running executable's path and classifies BOTH the raw (possibly symlink) path and its resolved target. The Homebrew cask installs a symlink in the brew prefix bin directory pointing into the Caskroom, so a managed classification on either path wins. When neither is managed, a manual classification on either path is accepted; otherwise the result is Ambiguous.

type Downloader

type Downloader struct {
	// BaseURL is the directory under which the release's assets and checksum
	// files live. When empty, the versioned GitHub release download directory
	// ("<defaultDownloadBaseURL>/v<version>") is used.
	BaseURL string
	// Client is the HTTP client used for requests. When nil, http.DefaultClient
	// is used.
	Client *http.Client
}

Downloader fetches and verifies release assets. BaseURL and Client are injectable so tests can target an httptest.Server.

func (Downloader) DownloadAndVerify

func (d Downloader) DownloadAndVerify(ctx context.Context, version, goos, goarch string) (string, error)

DownloadAndVerify downloads the release asset matching the given version and target OS/arch, verifies its sha256 against the release's per-OS checksum file, and — only on a successful match — extracts the ingitdb binary into a temp file whose path is returned. When goos/goarch are empty, runtime.GOOS/GOARCH are used.

Verification happens before any extraction. On a checksum mismatch or a missing/unfetchable checksum entry, it returns a non-nil error and an empty path, having touched no executable. This function never writes to or replaces the running executable; the swap is a separate concern.

type InstallMethod

type InstallMethod int

InstallMethod classifies how the running binary was installed.

const (
	// Managed means a package manager owns the binary; self-update is not eligible.
	Managed InstallMethod = iota
	// Manual means the binary was placed by the user (release archive or go install);
	// self-replace is eligible.
	Manual
	// Ambiguous means the location does not clearly indicate either method.
	Ambiguous
)

type Manager

type Manager int

Manager identifies a package manager that owns a managed install.

const (
	// ManagerNone means no package manager was detected.
	ManagerNone Manager = iota
	// Homebrew is the macOS/Linux Homebrew package manager. ingitdb is
	// distributed as a Homebrew cask, which installs the binary under a
	// Caskroom path and symlinks it into the brew prefix bin directory.
	Homebrew
	// Snap is the Linux snap package manager.
	Snap
)

type Resolver

type Resolver struct {
	// BaseURL is the releases endpoint. When empty, defaultReleasesURL is used.
	BaseURL string
	// Client is the HTTP client used for requests. When nil, http.DefaultClient
	// is used.
	Client *http.Client
}

Resolver fetches release information from GitHub. The base URL and HTTP client are injectable so tests can target an httptest.Server.

func (Resolver) LatestStableTag

func (r Resolver) LatestStableTag(ctx context.Context) (string, error)

LatestStableTag returns the tag of the newest non-prerelease, non-draft release. Releases are returned newest-first by the GitHub API; this skips prereleases and drafts and selects the first stable entry.

type Verdict

type Verdict int

Verdict is the outcome of comparing the current build against the latest stable release.

const (
	// UpToDate means the current version equals the latest stable release.
	UpToDate Verdict = iota
	// Available means a newer stable release exists.
	Available
	// Undetermined means the current version could not be established (e.g. a
	// dev build).
	Undetermined
)

func (Verdict) ExitCode

func (v Verdict) ExitCode() int

ExitCode maps a verdict to the process exit code `self-update --check` should use. UpToDate exits 0; Available and Undetermined exit 10.

Jump to

Keyboard shortcuts

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