Documentation
¶
Overview ¶
Package updater provides an in-app self-update facility for Wails v3 applications.
The package exposes a single Updater that is reachable as `app.Updater`. Configure it once via Init, then call Check / DownloadAndInstall / CheckAndInstall to drive the update flow.
Update sources are pluggable through the Provider interface. The Updater owns verification, atomic writes, the binary swap and the default window; providers only describe how to look up and stream a release.
Subscribe to lifecycle events through the standard Wails event system — both Go and JavaScript subscribe the same way:
app.Event.On(updater.EventDownloadProgress, func(e *application.CustomEvent) {
var p updater.Progress
_ = json.Unmarshal(e.JSON(), &p)
})
wails.Events.On("wails:updater:download-progress", (e) => { /* ... */ })
Index ¶
- Constants
- Variables
- func HandleHelperMode()
- type Artifact
- type BuiltinWindow
- type CheckRequest
- type Config
- type ErrorInfo
- type Host
- type Meta
- type Progress
- type Provider
- type Release
- type Stage
- type State
- type Updater
- func (u *Updater) Check(ctx context.Context) (*Release, error)
- func (u *Updater) CheckAndInstall(ctx context.Context) error
- func (u *Updater) CurrentVersion() string
- func (u *Updater) DownloadAndInstall(ctx context.Context) error
- func (u *Updater) DownloadedPath() string
- func (u *Updater) Init(cfg Config) error
- func (u *Updater) Restart(_ context.Context) error
- func (u *Updater) SkipVersion(v string)
- func (u *Updater) SkippedVersion() string
- func (u *Updater) State() State
- func (u *Updater) StopPeriodicCheck()
- type Verification
- type WindowHandle
- type WindowOption
- type WindowOptions
- type WindowSizer
Constants ¶
const ( // EventCheckStarted fires before a Check round trip. Payload: nil. EventCheckStarted = "wails:updater:check-started" // EventUpdateAvailable fires when Check returns a newer release. Payload: *Release. EventUpdateAvailable = "wails:updater:update-available" // EventNoUpdate fires when Check confirms the caller is up to date. Payload: nil. EventNoUpdate = "wails:updater:no-update" // EventDownloadStarted fires when the Updater begins streaming bytes from // a provider. Payload: *Release. EventDownloadStarted = "wails:updater:download-started" // EventDownloadProgress fires periodically during download (~10/sec). Payload: Progress. EventDownloadProgress = "wails:updater:download-progress" // EventDownloadComplete fires once all bytes are on disk and the file has // been closed, but BEFORE verification. Payload: *Release. EventDownloadComplete = "wails:updater:download-complete" // EventVerifying fires when the Updater begins verifying the downloaded // artifact. Payload: *Release. EventVerifying = "wails:updater:verifying" // EventInstalling fires when the Updater begins swapping the binary. // Payload: *Release. EventInstalling = "wails:updater:installing" // EventUpdateReady fires when an update is installed and a restart is // pending. Payload: *Release. EventUpdateReady = "wails:updater:update-ready" // EventError fires whenever any stage fails. Payload: ErrorInfo. EventError = "wails:updater:error" // EventMeta fires once per session before the first state-snapshot // replay, carrying host-side context the page can't derive from any // Release: the version currently running, and the version the user // has marked skipped (or "" if none). Payload: Meta. EventMeta = "wails:updater:meta" )
Event names emitted by the Updater. Subscribe in Go via app.Event.On(name, ...) or in JavaScript via wails.Events.On(name, ...). Payload types are documented inline next to each constant.
const ( EventWindowReady = "wails:updater:window:ready" EventUserInstall = "wails:updater:user:install" EventUserSkip = "wails:updater:user:skip" EventUserRemind = "wails:updater:user:remind" EventUserCancel = "wails:updater:user:cancel" EventUserRestart = "wails:updater:user:restart" )
User-action event names emitted by the default window and any custom template that follows the same contract. The Updater listens to these to drive the flow without ever calling into the window directly.
Variables ¶
var ( ErrAlreadyConfigured = errors.New("updater: Init already called") ErrNotConfigured = errors.New("updater: Init has not been called") ErrNoPendingRelease = errors.New("updater: no pending release (call Check first)") ErrDownloadInProgress = errors.New("updater: download already in progress") )
var ( // ErrNotReady is returned by Restart when there is no installed update // staged for launch. ErrNotReady = errors.New("updater: nothing to restart into (call DownloadAndInstall first)") )
Functions ¶
func HandleHelperMode ¶
func HandleHelperMode()
HandleHelperMode returns immediately when the current process was not spawned as an updater helper. When it WAS spawned as a helper it performs the swap, relaunches the application, and calls os.Exit — it never returns in that case.
The Wails application package calls this from application.New so that `app.Updater.Restart` works without users wiring anything by hand.
Types ¶
type Artifact ¶
type Artifact struct {
Filename string `json:"filename"`
Filetype string `json:"filetype,omitempty"`
Size int64 `json:"size,omitempty"`
Platform string `json:"platform,omitempty"`
Arch string `json:"arch,omitempty"`
}
Artifact describes the file to download for a Release on the running platform.
type BuiltinWindow ¶
type BuiltinWindow struct {
// HTML, if non-empty, replaces the default template entirely. The
// replacement is expected to listen to `updater:*` events and emit
// `updater:user:*` events using the standard Wails runtime — exactly
// what the default template does.
HTML string
// CSS, if non-empty, is appended to the default template inside a final
// <style> tag. Ignored when HTML overrides the template entirely.
CSS string
// Options overrides the chrome of the built-in window. Zero values fall
// back to the framework's sensible defaults (small, centred, resizable).
Options WindowOptions
}
BuiltinWindow customises the framework's default update window. Embed it in Config.Window when you want to override the HTML, layer extra CSS, or change the window chrome (frameless, size, etc.).
type CheckRequest ¶
CheckRequest carries platform context to a Provider's Check method. The Updater fills in defaults from runtime.GOOS / runtime.GOARCH when the user does not supply them via Config.
type Config ¶
type Config struct {
// CurrentVersion is the version currently running. Required.
// Pass the same string you use to tag releases (e.g. "1.2.3" — no "v" prefix).
CurrentVersion string
// Providers are the update sources, tried in order. The first to return
// a release is used; if a provider returns (nil, nil) the Updater treats
// the application as up to date and stops walking the chain (fallback is
// for "primary unreachable", not "providers disagree"). Required.
Providers []Provider
// PublicKey is the trust root used to verify release signatures. It is
// the ONLY trust anchor for signature verification — the release source
// cannot substitute its own key, since the whole point of pinning a key
// here is to bind verification to a value the application developer set
// at build time and the release feed cannot influence.
//
// When unset, releases that carry only a Digest still install (the digest
// is checked against the streaming hash), but any release that carries a
// Signature is rejected. Strongly recommended for any app whose
// distribution channel might be compromised or proxied.
PublicKey []byte
// CheckInterval, when non-zero, makes the Updater poll providers on a
// timer in the background. Pop-up-on-found behaviour is the same as a
// manual Check finding an update.
CheckInterval time.Duration
// Platform / Arch / Channel override the per-platform defaults passed to
// each Provider's Check. Leave empty to use runtime.GOOS / runtime.GOARCH
// and the provider's default channel.
Platform string
Arch string
Channel string
// Window controls how the update UI is rendered. Not yet wired in v1 of
// the package; see the upcoming Window option types.
Window WindowOption
}
Config configures the Updater. Pass it to Updater.Init.
type ErrorInfo ¶
type ErrorInfo struct {
Stage Stage `json:"stage"`
Message string `json:"message"`
Provider string `json:"provider,omitempty"`
}
ErrorInfo is the payload of EventError.
type Host ¶
type Host interface {
// Emit a custom event with the supplied data.
Emit(name string, data ...any) bool
// OnEvent registers a callback for a custom event. The returned function
// removes the listener.
OnEvent(name string, callback func(payload any)) func()
// OpenWindow creates and shows an update window. Implementations build
// a real Wails webview window from opts; tests return a recorder.
OpenWindow(opts WindowOptions) WindowHandle
// Quit asks the host application to begin its normal shutdown sequence.
// Called by Restart after the helper has been spawned so the helper's
// "wait for parent to exit" step actually completes.
Quit()
}
Host is the minimal surface the Updater needs from the application that owns it. The application package implements this via an adapter; tests stub it.
type Meta ¶
type Meta struct {
CurrentVersion string `json:"currentVersion"`
SkippedVersion string `json:"skippedVersion,omitempty"`
}
Meta is the payload of EventMeta — host-side context the default window template uses to render the "from" version in the update pill and the "v1.2.3 · This is the latest version" pill in the up-to-date state.
type Progress ¶
type Progress struct {
Written int64 `json:"written"`
Total int64 `json:"total"`
Rate float64 `json:"rate"` // bytes/sec smoothed over the last ~1s
Provider string `json:"provider,omitempty"`
}
Progress is the payload of EventDownloadProgress.
type Provider ¶
type Provider interface {
// Name identifies the provider in logs and in the `provider` field of
// progress / error event payloads. Should be stable and short
// (e.g. "github", "keygen.sh", "appcast").
Name() string
// Check returns the available upgrade for the running platform. Return
// (nil, nil) when the source confirms the caller is already up to date.
// Errors here put the Updater into the fallback chain.
Check(ctx context.Context, req CheckRequest) (*Release, error)
// Download streams the artifact bytes for r to dst. Providers should
// invoke onProgress periodically; it is never nil.
Download(ctx context.Context, r *Release, dst io.Writer, onProgress func(written, total int64)) error
}
Provider abstracts an update source. Implementations are typically tiny: resolve the next release for the running platform, then stream the bytes. Everything else (verification, atomic write, swap, restart, events) is the Updater's job.
type Release ¶
type Release struct {
Version string `json:"version"`
Channel string `json:"channel,omitempty"`
Name string `json:"name,omitempty"`
Notes string `json:"notes,omitempty"`
PublishedAt time.Time `json:"publishedAt,omitempty"`
Artifact Artifact `json:"artifact"`
Verification *Verification `json:"verification,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
// Provider is set by the Updater after Check; provider implementations
// should leave it empty. Used to route a follow-up Download back to the
// same source.
Provider string `json:"provider,omitempty"`
}
Release is what a Provider returns from Check when there is something newer.
type State ¶
type State string
State is the high-level lifecycle phase the Updater is currently in.
const ( StateUnconfigured State = "unconfigured" StateIdle State = "idle" StateChecking State = "checking" StateUpToDate State = "up-to-date" StateAvailable State = "available" StateDownloading State = "downloading" StateVerifying State = "verifying" StateInstalling State = "installing" StateReady State = "ready" StateError State = "error" )
type Updater ¶
type Updater struct {
// contains filtered or unexported fields
}
Updater is the singleton exposed as app.Updater. It is constructed during application initialisation but does nothing useful until Init is called.
func New ¶
New is for internal use by the application package and tests. End users obtain an Updater via app.Updater (or app.Updater.Init).
func (*Updater) Check ¶
Check walks the provider chain looking for an upgrade. Returns:
- (rel, nil): newer release found; payload of EventUpdateAvailable.
- (nil, nil): caller is up to date; payload of EventNoUpdate.
- (nil, err): all providers errored; err wraps every failure.
Fallback semantics: a provider returning (nil, nil) short-circuits to up-to-date — fallback exists for "primary unreachable", not "providers disagree about what's latest." A provider returning an error advances to the next one. If every provider errors, Check returns an error built from the chain.
func (*Updater) CheckAndInstall ¶
CheckAndInstall is the convenience method: it opens the update window (unless Config.Window == WindowNone) and runs Check + DownloadAndInstall. Returns nil with no side effects if the application is already up to date.
The window stays open for the duration of the flow AND across the "up-to-date" / error terminal states — the user dismisses it via the Close button. Opening + immediately closing on the no-update branch produced a perceptible flicker on every check; keeping the window up so the "You're up to date" panel actually renders matches what users expect from system-style updaters.
Apps that want silent background polling should use Config.Window = updater.WindowNone (no window ever opens) or invoke Check() directly and subscribe to EventNoUpdate / EventUpdateAvailable themselves.
func (*Updater) CurrentVersion ¶
CurrentVersion returns the version supplied at Init time, or "" if not configured.
func (*Updater) DownloadAndInstall ¶
DownloadAndInstall downloads the pending release (set by a previous Check), verifies it, and stages it for swap. Returns ErrNoPendingRelease if Check did not produce one. Returns ErrDownloadInProgress if another download is already running. The actual binary swap is performed in a follow-up commit; v1 of this branch stages the verified file and reports StateReady + EventUpdateReady.
func (*Updater) DownloadedPath ¶
DownloadedPath returns the on-disk path of the last successfully-installed (staged) update, or "" if none.
func (*Updater) Init ¶
Init configures the Updater. Returns ErrAlreadyConfigured if Init has already been called, or a validation error if cfg is malformed.
func (*Updater) Restart ¶
Restart performs the full restart-into-the-new-version dance: it spawns a helper-mode child (the same binary with sentinel env vars set) and then asks the host application to begin its shutdown sequence via Host.Quit. Once the running process exits, the helper performs the binary swap and relaunches the (now-replaced) application.
Returns ErrNotReady if DownloadAndInstall has not produced an installed artifact yet. If the helper spawn fails the error is surfaced and Quit is not called — the caller's process stays alive on the old binary.
On success Restart returns once the helper has started and Quit has been dispatched. The caller's process will exit asynchronously as the host's normal shutdown unwinds.
func (*Updater) SkipVersion ¶
SkipVersion records the supplied version as skipped — subsequent Checks will treat that version as "no update." Used by the default window's Skip This Version button and by callers driving headless flows.
func (*Updater) SkippedVersion ¶
SkippedVersion returns the currently-skipped version, if any.
func (*Updater) StopPeriodicCheck ¶
func (u *Updater) StopPeriodicCheck()
StopPeriodicCheck cancels the timer started by Init when Config.CheckInterval > 0 and blocks until the polling goroutine has returned (so callers can safely inspect provider state afterward). Safe to call when no periodic check was configured.
type Verification ¶
type Verification struct {
DigestAlgo string `json:"digestAlgo,omitempty"` // "sha256", "sha512"
Digest []byte `json:"digest,omitempty"` // raw digest bytes
SignatureAlgo string `json:"signatureAlgo,omitempty"` // "ed25519", "ed25519ph", "ecdsa-p256"
Signature []byte `json:"signature,omitempty"` // raw signature bytes
}
Verification carries everything the Updater needs to authenticate the downloaded bytes. A Provider populates this from the release source; the Updater verifies against it using its configured trust root.
Either Digest+DigestAlgo, Signature+SignatureAlgo, or both may be present. When both are present, both are checked; either failing fails the update.
Signature verification always uses Config.PublicKey as the trust root. The release source has no say in which key authenticates it — that is the entire point of pinning a key out-of-band at build time. Releases that ship a Signature without a configured Config.PublicKey fail closed.
type WindowHandle ¶
WindowHandle is the minimal API the Updater drives once a window is open. The application adapter satisfies this around a *WebviewWindow.
SetHTML is deliberately omitted — loading HTML after construction puts it on about:blank where the Wails runtime isn't injected (so JS event emits no-op on webkit2gtk and in some WebView2 configurations). Templates are passed via WindowOptions.InitialHTML at construction time instead.
type WindowOption ¶
type WindowOption interface {
// contains filtered or unexported methods
}
WindowOption is the marker interface for the Window slot on Config. The set of concrete types is small and closed: BuiltinWindow, application.Window (BYO), or the sentinel WindowNone. The full window machinery is implemented in a follow-up commit; the interface is declared here so Config does not change shape between commits.
var WindowNone WindowOption = windowNoneType{}
WindowNone selects headless mode: the Updater drives the flow but never asks the host to open a window. Subscribe to events from your own UI.
func BYOWindow ¶
func BYOWindow(w WindowHandle) WindowOption
BYOWindow wraps a caller-owned WindowHandle (typically an *application.WebviewWindow created outside the updater) so it can be passed via Config.Window. The Updater drives Show / Close / EmitEvent on the wrapped handle instead of creating its own window.
The WindowOption interface has an unexported marker method that prevents arbitrary types from being assigned to Config.Window directly; this constructor is the bridge — call it once at Init time:
myWin := app.Window.NewWithOptions(application.WebviewWindowOptions{...})
app.Updater.Init(updater.Config{
Window: updater.BYOWindow(myWin),
...,
})
type WindowOptions ¶
type WindowOptions struct {
Title string
Width, Height int
Frameless bool
AlwaysOnTop bool
DisableResize bool
InitialHTML string
}
WindowOptions describes the chrome and starting content for a window the Updater asks the host to open. Maps to (a subset of) application.WebviewWindowOptions on the host side.
type WindowSizer ¶
type WindowSizer interface {
SetSize(width, height int)
}
WindowSizer is an optional capability a WindowHandle may implement to allow the Updater to resize the window in response to state changes. The default window template uses this to shrink the Up-to-Date state to a compact card; the framework adapter for *application.WebviewWindow implements it transparently. BYO windows can opt in by adding the SetSize method themselves; if they don't, the Updater silently skips the resize and the window stays whatever size the host opened it at.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
semver
Package semver wraps golang.org/x/mod/semver with a small, provider-friendly interface.
|
Package semver wraps golang.org/x/mod/semver with a small, provider-friendly interface. |
|
providers
|
|
|
appcast
Package appcast implements an updater.Provider against a Sparkle-style AppCast RSS feed.
|
Package appcast implements an updater.Provider against a Sparkle-style AppCast RSS feed. |
|
github
Package github implements an updater.Provider backed by GitHub Releases.
|
Package github implements an updater.Provider backed by GitHub Releases. |
|
keygen
Package keygen implements an updater.Provider against the keygen.sh REST API.
|
Package keygen implements an updater.Provider against the keygen.sh REST API. |