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
- Variables
- func InstallPackage(ctx context.Context, kind, file string) error
- func Newer(a, b string) bool
- func PackageCommand(kind, file string, has func(string) bool) []string
- func PackageKind() string
- type Install
- type InstallProgress
- type Method
- type Phase
- type Release
- type Updater
- func (u *Updater) Apply(ctx context.Context) (VersionInfo, error)
- func (u *Updater) Check(ctx context.Context, refresh bool) (*Release, error)
- func (u *Updater) DownloadPackage(ctx context.Context, kind string) (string, string, error)
- func (u *Updater) Finish() error
- func (u *Updater) Info(ctx context.Context, refresh bool) VersionInfo
- func (u *Updater) Plan(ctx context.Context) (VersionInfo, error)
- func (u *Updater) Restarting() bool
- func (u *Updater) Serve(ctx context.Context) error
- func (u *Updater) String() string
- type VersionInfo
Constants ¶
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).
const DefaultAPI = "https://api.github.com/repos/akamachinecode/exhale"
DefaultAPI is the GitHub API of Exhale's repository.
Variables ¶
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 ¶
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 ¶
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 ¶
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.
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 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 ¶
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 ¶
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 ¶
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 ¶
Restarting reports whether the server stopped to start a new version.
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.