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:
- repoFull arg ("owner/repo"), typically baked from --release-github-repo
- fallback to debug.ReadBuildInfo() Main.Path when arg is empty (lets a "same source repo as releases" setup work without a flag)
- else updater is disabled — Configured() returns false and CheckNow returns an error.
Index ¶
- func CleanupOldBinary()
- type LatestInfo
- type Result
- type Sentinel
- type UpdateOutcome
- type Updater
- func (u *Updater) ApplyStagedAndRestart(stops ...func()) error
- func (u *Updater) CheckLatest(ctx context.Context) (LatestInfo, error)
- func (u *Updater) CheckNow(ctx context.Context) (Result, error)
- func (u *Updater) CheckUpdateOutcome(runningVersion string) (*UpdateOutcome, error)
- func (u *Updater) ClearOutcome()
- func (u *Updater) Configured() bool
- func (u *Updater) Download(ctx context.Context, info LatestInfo) error
- func (u *Updater) HasStaged() bool
- func (u *Updater) StagedVersion() string
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
StagedVersion is the tag (e.g. "v1.2.3") of the pending update.