selfupdate

package
v0.0.24 Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package selfupdate implements in-process binary self-update for the runner.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BinaryVersion added in v0.0.19

func BinaryVersion(ctx context.Context, path string) string

BinaryVersion runs `<path> version` and parses the first line, which every released runner prints as "flashduty-runner <version>". Returns "" (not an error) when the file doesn't exist, so callers can treat absent-canonical and unreadable-version identically: don't redirect.

func NewerVersion added in v0.0.19

func NewerVersion(candidate, own string) bool

NewerVersion reports whether candidate is a parseable version strictly newer than own. Either side unparseable → false: a dev build never trampolines, and a canonical binary whose version can't be read is never exec'd into.

func RestartInto added in v0.0.19

func RestartInto(path string) error

RestartInto hands the process over to the binary at path (exec on unix, spawn-and-exit on windows). Exposed for the boot trampoline in cmd.

func SeedCanonical added in v0.0.19

func SeedCanonical(exe, canonical string) error

SeedCanonical copies the running (old) binary to the canonical path if nothing is there yet. Run before the first relocated Apply so the swap's .bak snapshot — and therefore probation rollback — has a real predecessor: rolling back from a bad new version lands on this copy of the version that was running, not on a missing file. Copies via a temp file + rename so a crash mid-copy can never leave a truncated binary at the canonical path.

func ShouldSkipTarget

func ShouldSkipTarget(exePath, target string) bool

ShouldSkipTarget reports whether an advertised target should be ignored because this host already rolled back from exactly that version.

Types

type Marker

type Marker struct {
	TargetVersion string `json:"target_version"`
	Attempts      int    `json:"attempts"`
	RolledBack    bool   `json:"rolled_back"`
}

Marker persists across re-exec/restart to drive the probation/rollback state machine. A live marker means "an upgrade to TargetVersion is on probation"; it is deleted on commit (handshake) and carries RolledBack=true across the rollback re-exec so the restored old binary can report the outcome once.

type Outcome

type Outcome struct {
	InProbation bool // main should Commit() after first successful handshake
}

Outcome tells main what to do after CheckOnBoot.

type ProbationMgr

type ProbationMgr struct {
	ExePath        string
	CurrentVersion string
	MaxAttempts    int
	// contains filtered or unexported fields
}

ProbationMgr runs the boot-time probation/rollback state machine driven by the persisted upgrade marker. reExec is an injectable seam so tests never actually restart; nil means the real platform restartSelf.

func NewProbationMgr

func NewProbationMgr(exePath, currentVersion string, maxAttempts int) *ProbationMgr

NewProbationMgr returns a ProbationMgr for the given canonical executable.

func (*ProbationMgr) CheckOnBoot

func (p *ProbationMgr) CheckOnBoot() (Outcome, error)

CheckOnBoot inspects the marker at startup and advances the probation state machine: no marker -> normal; committed/stale -> cleanup; rolled_back -> report; matching target not yet committed -> probation (attempts++ then rollback past MaxAttempts).

func (*ProbationMgr) Commit

func (p *ProbationMgr) Commit()

Commit is called by main after the first successful WS handshake during probation. It clears the marker, the .bak rollback copy, and any prior failed-version memory (we've successfully moved to a new version, so older failures are moot).

func (*ProbationMgr) RollbackNow

func (p *ProbationMgr) RollbackNow()

RollbackNow restores the previous binary and re-execs into it. main calls this when the probation deadline elapses without a successful handshake (failure mode ②: the new binary boots but cannot complete the WS handshake — auth/url/ protocol regression). It is independent of the boot-attempt counter, which can never fire under the unlimited-reconnect (maxAttempts=0) config because the process never exits.

type StatusFunc

type StatusFunc func(phase, errMsg string)

StatusFunc reports an upgrade phase (and optional error message) to the caller.

type Target added in v0.0.19

type Target struct {
	Exe       string // running executable, symlinks resolved
	Path      string // upgrade target; "" if no writable location exists at all
	Relocated bool   // Path is the canonical state-home fallback, not Exe
}

Target is where self-update operates for this process.

Path is the binary path upgrades download to, swap at, and re-exec from — and therefore also where the probation marker, .bak rollback copy, and failed-version memory live (layoutFor anchors on it). When the running executable's own directory is writable, Path == Exe and upgrades swap in place (systemd state-dir installs, root installs, dev builds). When it is not — a root-owned /usr/local/bin binary run as a regular user — Path falls back to the canonical runner-owned location <state home>/bin, which the boot trampoline in cmd/main.go redirects into on every later start.

func ResolveTarget added in v0.0.19

func ResolveTarget(exePath, stateHome string) Target

ResolveTarget decides where self-update should operate, given the running executable path and the runner state home (environment.RunnerStateHome()).

exePath is symlink-resolved first: on Linux os.Executable() already returns the /proc/self/exe target, but on darwin it returns the invocation path, so an installer layout that symlinks /usr/local/bin -> <state dir>/bin would otherwise be misjudged by the writability probe.

The canonical fallback directory is created on demand; this is the same <state home>/bin that holds the bundled fduty CLI, guaranteed writable by the runner's own user. Note it deliberately ignores FLASHDUTY_RUNNER_BIN_DIR: that override exists to point bash's PATH at an image-baked fduty (e.g. /usr/local/bin on cloud images) and may well be read-only — the runner binary's home must stay in the user-writable state tree.

type Updater

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

Updater performs an in-process self-update: download, verify, extract, atomic swap, marker write, and restart. reExec is an injectable seam so tests never actually restart; nil means the real platform restartSelf (exec on unix, spawn-and-exit on windows).

func NewUpdater

func NewUpdater(exePath string) *Updater

NewUpdater returns an Updater bound to the binary path upgrades operate on — Target.Path from ResolveTarget: the running executable for in-place updates, or the canonical state-home location for relocated ones.

func (*Updater) Apply

func (u *Updater) Apply(ctx context.Context, p protocol.UpgradePayload, status StatusFunc) error

Apply downloads the target artifact, verifies it, atomically swaps it into place, writes the probation marker, and re-execs. On any failure before the swap it leaves the running binary untouched; on a post-swap re-exec failure it rolls back inline.

Jump to

Keyboard shortcuts

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