updater

package
v0.14.21 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package updater downloads, verifies, and applies new release binaries from GitHub. The current binary embeds a target release repo + PAT (set at build time via `wick build --release-github-repo ...` and `--release-github-pat ...`); at startup the system tray asks this package whether a staged update is pending (apply + re-exec) or whether to fetch a newer release in the background.

Asset naming convention (must match the release CI workflow):

<appName>-darwin-<GOARCH>.dmg              macOS disk image
<appName>-linux-<GOARCH>.deb               Debian package
<appName>-windows-<GOARCH>.exe             Windows binary
<asset>.sha256                             checksum sibling

The downloaded asset is extracted to its inner binary (per-OS via extractStaged) before being written to the staged path; .exe is a pass-through.

Repo resolution:

  1. repoFull arg ("owner/repo"), typically baked from --release-github-repo
  2. fallback to debug.ReadBuildInfo() Main.Path when arg is empty (lets a "same source repo as releases" setup work without a flag)
  3. else updater is disabled — Configured() returns false and CheckNow returns an error.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupOldBinary

func CleanupOldBinary()

CleanupOldBinary removes any leftover <exe>.old from a prior Windows update swap. Safe to call from startup; quietly ignores "file in use" errors (Windows will purge on reboot).

Types

type LatestInfo

type LatestInfo struct {
	Version       string // normalised "vX.Y.Z"
	AlreadyLatest bool   // true when current >= latest (Download is a no-op)
	AlreadyStaged bool   // true when this exact version is already staged on disk
	// contains filtered or unexported fields
}

LatestInfo describes the GitHub release latest tag plus the assets that match this binary's GOOS/GOARCH. Returned by CheckLatest so the caller (tray) can show the version before kicking off the download.

type Result

type Result struct {
	LatestVersion string
	Downloaded    bool
	AlreadyLatest bool
}

Result is what CheckNow returns to the caller. The tray uses Downloaded to show "Restart now (vX)" and AlreadyLatest to log a quiet "you're current" line for a manual click.

type Sentinel added in v0.9.0

type Sentinel struct {
	FromVersion   string    `json:"from_version"`
	ToVersion     string    `json:"to_version"`
	StartedAt     time.Time `json:"started_at"`
	Method        string    `json:"method"` // "msi", "dpkg", "binary-swap"
	InstallerLog  string    `json:"installer_log,omitempty"`
	ExpectedPath  string    `json:"expected_path,omitempty"`
	ExpectedSHA   string    `json:"expected_sha,omitempty"`
	HelperScript  string    `json:"helper_script,omitempty"`
	HelperLog     string    `json:"helper_log,omitempty"`
	OldBinaryPath string    `json:"old_binary_path,omitempty"`
}

Sentinel records what an in-flight update is supposed to produce. Written by ApplyStagedAndRestart just before the swap; read by the next process launch to decide if the update succeeded, partially failed, or got stuck. The sentinel is the single source of truth for "did the install actually work" — installer exit codes are not trusted because msiexec /qn, dpkg postinst, and inner-binary swaps all have ways to silently no-op.

type UpdateOutcome added in v0.9.0

type UpdateOutcome struct {
	Pending      bool      // sentinel exists, install still in flight (helper running)
	Success      bool      // running version matches sentinel ToVersion
	VersionMatch bool      // running version == sentinel ToVersion
	Stale        bool      // sentinel older than 10 minutes — assume helper died
	From         string    // sentinel FromVersion
	To           string    // sentinel ToVersion
	StartedAt    time.Time // sentinel StartedAt
	InstallerLog string    // path to installer log for diagnosis
	HelperLog    string    // path to helper script log for diagnosis
	Reason       string    // human-readable summary
}

UpdateOutcome describes what happened to a previously-staged update, derived by comparing the sentinel against the running binary.

type Updater

type Updater struct {
	// contains filtered or unexported fields
}

Updater is safe for concurrent use. CheckNow guards itself with a "in flight" flag so background and manual triggers don't double-fire.

func New

func New(cfg *userconfig.Config, save func() error, appName, currentVersion, repoFull, pat string) (*Updater, error)

New constructs an Updater. cfg + save let the updater persist staged-update state into the same user-config file the tray uses for its other prefs, so a quit-and-relaunch picks up the staged binary without re-downloading.

func (*Updater) ApplyStagedAndRestart

func (u *Updater) ApplyStagedAndRestart(stops ...func()) error

ApplyStagedAndRestart performs the binary swap and re-execs the new process. Caller passes stop funcs (server cancel, worker cancel) so goroutines drain before the swap. On success this function does not return — Unix syscall.Exec replaces our image; Windows spawns a new process and we os.Exit. Returns an error only when the swap itself fails before re-exec.

Before handing control to the per-OS swap, an update sentinel is written to cacheDir recording the expected post-install version. The next launch reads it via CheckUpdateOutcome to decide if the install succeeded — installer exit codes alone are not trusted (msiexec /qn, dpkg postinst, and inner-binary swaps all have ways to silently no-op).

func (*Updater) CheckLatest

func (u *Updater) CheckLatest(ctx context.Context) (LatestInfo, error)

CheckLatest fetches the latest release and compares it to the running version. It does NOT download — call Download with the returned LatestInfo to actually fetch the asset. Concurrent calls are coalesced.

func (*Updater) CheckNow

func (u *Updater) CheckNow(ctx context.Context) (Result, error)

CheckNow runs CheckLatest then Download in one shot — convenience for the background auto-update goroutine that doesn't need intermediate UI feedback.

func (*Updater) CheckUpdateOutcome added in v0.9.0

func (u *Updater) CheckUpdateOutcome(runningVersion string) (*UpdateOutcome, error)

CheckUpdateOutcome inspects the sentinel and the running binary and returns a verdict. Callers (tray) use this on startup to log success or surface failure — and to clear the sentinel once handled.

runningVersion is the version baked into the current binary. If empty (dev build) we treat any sentinel as "can't verify" rather than failure, because comparing against "" gives no information.

func (*Updater) ClearOutcome added in v0.9.0

func (u *Updater) ClearOutcome()

ClearOutcome removes the sentinel. Call after CheckUpdateOutcome and after the caller has logged / surfaced the result so a stale sentinel doesn't keep firing the same notification on every launch.

func (*Updater) Configured

func (u *Updater) Configured() bool

Configured reports whether a release source is known. False means the updater can't do anything — caller should hide UI affordances.

func (*Updater) Download

func (u *Updater) Download(ctx context.Context, info LatestInfo) error

Download fetches the binary asset described by info, verifies its SHA256 against the sibling .sha256 file, and stages it under the updater's cache dir. Persists the staged path/version into the userconfig so a subsequent Apply or auto-apply on next launch can pick it up. No-op if info indicates AlreadyLatest or AlreadyStaged.

func (*Updater) HasStaged

func (u *Updater) HasStaged() bool

HasStaged returns true when a previously downloaded binary is still on disk and waiting to be applied. Stale config rows that point at a missing file are treated as no-staged (and should be cleared by the caller).

func (*Updater) StagedVersion

func (u *Updater) StagedVersion() string

StagedVersion is the tag (e.g. "v1.2.3") of the pending update.

Jump to

Keyboard shortcuts

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