Help us shape the future of Go! Take the 2026 Go Developer Survey

upgrade

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: AGPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package upgrade replaces a bare-binary Instance's binary with a newer release without stopping it (ADR 0033, ADR 0034): it verifies the archive, unpacks the new binary next to the old one, runs the new binary's `migrate` while the old one still serves, and only then swaps the file atomically, keeping the old one as `.old`.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNeedsConfirmation: the release has Upgrade notes and --yes was not given.
	ErrNeedsConfirmation = errors.New("the release has Upgrade notes: read them above and rerun with --yes")
	// ErrDowngrade: the requested release is older than the running one.
	ErrDowngrade = errors.New("refusing to downgrade")
)

Errors a caller maps to an exit code or a message.

Functions

func ArchiveName

func ArchiveName(version string, p Platform) string

ArchiveName is the release archive for a version (no leading "v") and platform, as the release build names it.

func DetectContainer

func DetectContainer() string

DetectContainer says "docker" inside a container and "helm" on Kubernetes, "" otherwise.

func PrintRestart

func PrintRestart(w io.Writer, r Result, service string) error

PrintRestart is the default post-swap step: it names the one command left to the user.

Types

type Boundaries

type Boundaries struct {
	State  MajorFloor   `json:"state"`
	Majors []MajorFloor `json:"majors"`
}

Boundaries is what `sphericon migrate --status --json` prints and the walk reads: the database's schema state and the binary's table of Majors, oldest first, where the Floor of entry N+1 is the last release of Major N (internal/db.Majors).

type GitHub

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

GitHub reads the public GitHub Releases of the project. It is a Source for `upgrade`, and its LatestRelease is the one question "is there a newer release" that the Release check (ADR 0032) and `upgrade --check` share.

func NewGitHub

func NewGitHub() (*GitHub, error)

NewGitHub points at api.github.com. It honours HTTP_PROXY, HTTPS_PROXY and NO_PROXY.

func NewGitHubAt

func NewGitHubAt(baseURL string) (*GitHub, error)

NewGitHubAt points at another API root (a test server, GitHub Enterprise); "" is api.github.com.

func (*GitHub) Fetch

func (g *GitHub) Fetch(ctx context.Context, version string, p Platform) (Release, error)

Fetch downloads the archive and checksums.txt of a release into a temporary directory.

func (*GitHub) LatestRelease

func (g *GitHub) LatestRelease(ctx context.Context) (string, error)

LatestRelease is the newest stable release (GitHub's `latest`: no prerelease, no draft), without the leading "v".

func (*GitHub) Versions

func (g *GitHub) Versions(ctx context.Context, p Platform) ([]string, error)

Versions lists the stable releases that carry an archive for the platform.

type MajorFloor

type MajorFloor struct {
	Major int    `json:"major"`
	Floor string `json:"floor"`
}

MajorFloor is one Major and the oldest release that runs on its schema.

type Options

type Options struct {
	Source   Source
	Target   string   // path of the installed binary
	Current  string   // running version, "v0.1.0" or "0.1.0"
	Version  string   // pinned release; empty = the newest the Source has
	Platform Platform // zero = this OS and architecture
	Yes      bool     // go past Upgrade notes
	Out      io.Writer
	// Service is the systemd unit the restart hint names; "" = "sphericon".
	Service string

	// Container names the command to use instead when running inside a container or on
	// Kubernetes ("docker" or "helm"), "" on a bare host; nil = detect from the environment.
	Container func() string
	// Run executes a binary, writing its stdout to stdout; nil = os/exec with the
	// process environment and stderr passed through.
	Run func(ctx context.Context, bin string, args []string, stdout io.Writer) error
	// PostSwap runs after the swap; nil = print the restart command. A later `--restart`
	// replaces it with one that restarts the systemd service.
	PostSwap func(ctx context.Context, r Result) error
	// contains filtered or unexported fields
}

Options is one Upgrade step.

type Platform

type Platform struct{ OS, Arch string }

Platform is the OS and architecture a release archive is built for.

type Release

type Release struct {
	Version       string // without the leading "v"
	ArchiveName   string
	ArchivePath   string
	ChecksumsPath string
	// Cleanup removes what the source staged; nil when nothing was (a directory source).
	Cleanup func()
}

Release is one release's archive and checksums.txt, staged on local disk.

type Result

type Result struct {
	Changed bool   // false when the Instance already runs the release
	Version string // installed release, without the leading "v"
	Binary  string // the installed path
	Old     string // the previous binary, kept next to it
}

Result is what a step left on disk.

func Step

func Step(ctx context.Context, o Options) (Result, error)

Step runs one Upgrade: to the pinned release, or to the newest the Source has.

func Walk

func Walk(ctx context.Context, o Options) ([]Result, error)

Walk upgrades to the target (Options.Version, or the newest the Source has), going first to the last release of each Major the database has yet to cross, every step a full Step. Inside one Major that is a single step.

The table of Majors lives in the release being installed, not in the running binary, so the walk stages the target, asks it (`migrate --status --json`, which also reads the database) and then installs the Floor of every Major above the database's, oldest first. It needs nothing beyond the target's own archive, so it works offline. That same staged binary carries the changelog of every release up to itself, so the walk prints the Upgrade notes of every release it will install, and waits for --yes, before the first migration or swap; the target is then installed from the staged copy rather than fetched again.

With a directory Source every archive of the walk is checked before the first step, and a missing one refuses the whole Upgrade.

type Source

type Source interface {
	// Versions lists the releases it can supply for the platform (without the leading "v").
	Versions(ctx context.Context, p Platform) ([]string, error)
	// Fetch stages one release (version without the leading "v") for the platform.
	Fetch(ctx context.Context, version string, p Platform) (Release, error)
}

Source is where releases come from: a directory of downloaded files, or GitHub Releases. The Major walk asks it for versions and fetches one per step.

func DirSource

func DirSource(dir string) Source

DirSource reads archives and checksums.txt already on the machine (`upgrade --from <dir>`).

type Systemd

type Systemd struct {
	Service string // unit name, "sphericon"
	Out     io.Writer
}

Systemd is `upgrade --restart`: it runs the new binary's `migrate` as a transient unit with the service's own User and EnvironmentFile (so the Self-hoster installs no second unit file, ADR 0034), and restarts the service once the binary is swapped.

func (Systemd) PostSwap

func (s Systemd) PostSwap(ctx context.Context, r Result) error

PostSwap is Options.PostSwap: it restarts the service and fails when it is not active.

func (Systemd) Run

func (s Systemd) Run(ctx context.Context, bin string, args []string, stdout io.Writer) error

Run is Options.Run: `migrate` (and `migrate --status --json`, which reads the database) goes through systemd-run, everything else runs directly.

Jump to

Keyboard shortcuts

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