update

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: AGPL-3.0 Imports: 24 Imported by: 0

Documentation

Overview

Package update keeps Exhale up to date from its GitHub releases (ADR 0033). It finds the newest release, tells how this copy of Exhale was installed, and where Exhale may replace itself (the macOS app, the Windows app, a binary in a folder it can write) it downloads the new version, checks it against the release's checksums, puts it in place and restarts. Elsewhere (Docker, the Linux packages, a binary owned by root) it says which command to run.

Index

Constants

View Source
const (
	ReasonDevelopment = "development" // a build from source, with no version
	ReasonManual      = "manual"      // Docker and the packages: updated from outside
	ReasonReadOnly    = "read-only"   // Exhale may not write where it is installed
	ReasonUnsupported = "unsupported" // no installer for this system
)

Reasons why Exhale cannot update itself (Info.Reason).

View Source
const DefaultAPI = "https://api.github.com/repos/akamachinecode/exhale"

DefaultAPI is the GitHub API of Exhale's repository.

Variables

View Source
var (
	ErrBusy     = errors.New("update: an update is already under way")
	ErrUpToDate = errors.New("update: this is already the newest version")
	ErrCannot   = errors.New("update: Exhale cannot update itself here")
	ErrNotYet   = errors.New("update: GitHub has not been asked yet")
)

Errors of Apply.

Functions

func InstallPackage

func InstallPackage(ctx context.Context, kind, file string) error

InstallPackage installs a downloaded package as root through pkexec, which shows the system's password dialog. The package restarts the service; the window follows its program (package window).

func Newer

func Newer(a, b string) bool

Newer reports whether version a comes after b. A build without a version (dev) is never older than anything: it does not update.

func PackageCommand

func PackageCommand(kind, file string, has func(string) bool) []string

PackageCommand is the package manager's command that installs file.

func PackageKind

func PackageKind() string

PackageKind is the kind of package Exhale was installed from on this machine: deb when dpkg knows it, else rpm.

Types

type Install

type Install struct {
	Method  Method
	Exe     string // this program, symlinks resolved
	Bundle  string // macOS: the Exhale.app that holds Exe
	Package string // deb or rpm
	Service bool   // a systemd service set up by install.sh --service
	// Writable: Exhale may put a new version where it is installed.
	Writable bool
	GOOS     string
}

Install is this copy of Exhale: how it was installed and whether it may replace itself.

func Detect

func Detect(exe string, desktopApp bool) Install

Detect finds out how the program at exe was installed. desktopApp is set in the builds of Exhale.app and Exhale.exe.

type InstallProgress

type InstallProgress struct {
	Phase   Phase  `json:"phase" enum:"idle,downloading,installing,restarting,failed"`
	Version string `json:"version,omitempty" jsonschema:"the version being installed"`
	Done    int64  `json:"done" jsonschema:"bytes downloaded"`
	Total   int64  `json:"total" jsonschema:"bytes to download, 0 when unknown"`
	Error   string `json:"error,omitempty" jsonschema:"why the update failed; Exhale kept running the version it had"`
}

InstallProgress is an update under way, or the last one that failed.

type Method

type Method string

Method is how this copy of Exhale was installed, which decides how it updates.

const (
	MethodMacApp  Method = "macos-app"   // Exhale.app, from Exhale.dmg
	MethodWindows Method = "windows-app" // Exhale.exe, from Exhale-Setup.exe
	MethodBinary  Method = "binary"      // the exhale binary from an archive or install.sh
	MethodPackage Method = "package"     // the .deb or .rpm
	MethodDocker  Method = "docker"      // the container image
)

type Phase

type Phase string

Phase is where an update is.

const (
	PhaseIdle        Phase = "idle"
	PhaseDownloading Phase = "downloading"
	PhaseInstalling  Phase = "installing"
	PhaseRestarting  Phase = "restarting"
	PhaseFailed      Phase = "failed"
)

type Release

type Release struct {
	Version   string
	URL       string
	Notes     string
	Published time.Time
	Assets    map[string]string // file name → download address
}

Release is a published version of Exhale.

type Updater

type Updater struct {
	Version string  // running now
	Install Install // how this copy was installed
	API     string  // GitHub API of the repository (DefaultAPI)
	Client  *http.Client
	Dir     string        // work folder for downloads: <data dir>/update
	Every   time.Duration // how often Serve checks; 0 never
	// OnlyWhenAsked: GitHub is asked only by a refresh (Check now), never
	// by itself (--no-update-check).
	OnlyWhenAsked bool
	Log           *slog.Logger
	// Restart stops the server; its caller then calls Finish, which starts
	// the new version. Without it, Exhale never updates itself.
	Restart func()
	Now     func() time.Time
	// contains filtered or unexported fields
}

Updater checks for new versions and installs them.

func (*Updater) Apply

func (u *Updater) Apply(ctx context.Context) (VersionInfo, error)

Apply starts installing the newest release, in the background: Info follows it. When the new version is in place, the server restarts.

func (*Updater) Check

func (u *Updater) Check(ctx context.Context, refresh bool) (*Release, error)

Check asks GitHub for the newest release, unless it was asked recently. refresh asks again unless it was asked within the last minute.

func (*Updater) DownloadPackage

func (u *Updater) DownloadPackage(ctx context.Context, kind string) (string, string, error)

DownloadPackage downloads the newest .deb or .rpm into Dir when it is newer than Version, checked against the release's checksums. It returns the file and its version.

func (*Updater) Finish

func (u *Updater) Finish() error

Finish starts the new version once the server has stopped: the new binary takes this process's place, or the Windows installer runs. It does nothing when no update is waiting.

func (*Updater) Info

func (u *Updater) Info(ctx context.Context, refresh bool) VersionInfo

Info says what is installed, what is newest and how to update.

func (*Updater) Plan

func (u *Updater) Plan(ctx context.Context) (VersionInfo, error)

Plan checks that Apply would start, without starting it (a dry run).

func (*Updater) Restarting

func (u *Updater) Restarting() bool

Restarting reports whether the server stopped to start a new version.

func (*Updater) Serve

func (u *Updater) Serve(ctx context.Context) error

Serve checks for a new version a minute after the start, then every Every. It also clears what an earlier update left in Dir.

func (*Updater) String

func (u *Updater) String() string

type VersionInfo

type VersionInfo struct {
	Current     string          `json:"current" jsonschema:"the version running now; dev for a build from source"`
	Latest      string          `json:"latest,omitempty" jsonschema:"the newest release, once GitHub has been asked"`
	Available   bool            `json:"available" jsonschema:"the newest release is newer than this one"`
	NotesURL    string          `json:"notesUrl,omitempty" jsonschema:"the release's page, with what changed"`
	Notes       string          `json:"notes,omitempty" jsonschema:"what changed, in Markdown"`
	PublishedAt string          `json:"publishedAt,omitempty" jsonschema:"when the newest release came out, RFC 3339"`
	CheckedAt   string          `json:"checkedAt,omitempty" jsonschema:"when GitHub was last asked, RFC 3339"`
	CheckError  string          `json:"checkError,omitempty" jsonschema:"why the last check failed"`
	Method      Method          `json:"method" enum:"macos-app,windows-app,binary,package,docker" jsonschema:"how this copy of Exhale was installed"`
	CanApply    bool            `json:"canApply" jsonschema:"apply_update can install the newest release here"`
	Reason      string          `json:"reason,omitempty" enum:",development,manual,read-only,unsupported" jsonschema:"why Exhale cannot update itself here"`
	Command     string          `json:"command,omitempty" jsonschema:"what to run to update instead, when Exhale cannot do it itself"`
	Download    string          `json:"download,omitempty" jsonschema:"the file to download by hand instead"`
	Progress    InstallProgress `json:"progress"`
}

VersionInfo is what get_update answers.

Jump to

Keyboard shortcuts

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