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

wmlupdate

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

WML Update

WML Update

Apache-2.0 Docs Go Reference

A lightweight update library for Go applications, with a release CLI and an optional sidecar for other languages.

Embed it as a dependency and expose only two operations to your app:

update, err := updater.Check(ctx)
if update.Available {
    result, err := updater.Apply(ctx, update, wmlupdate.ApplyOptions{})
}

No daemon. No framework. No global registration. No required application architecture. The host app keeps control of its UI, lifecycle and restart.

中文文档

Why a library

Application updaters are deceptively hard: interrupted downloads, tampered artifacts, three filesystem models, running Windows executables, rollback, crash recovery, signatures and progress reporting. WML Update packages those concerns behind a deliberately small Go API:

u, err := wmlupdate.New(wmlupdate.Options{
    ConfigPath:     "updater.json",
    CurrentVersion: appVersion,
})
if err != nil { return err }
defer u.Close()

update, err := u.Check(ctx)
if err != nil || !update.Available { return err }

result, err := u.Apply(ctx, update, wmlupdate.ApplyOptions{})

Check is read-only. Apply installs the exact checked release, downloading and verifying the artifact. Progress is an optional callback. The terminal result is the return value.

The same engine is exposed as:

  • a release CLI for packaging, signing, manifests and checksums;
  • an optional sidecar CLI + NDJSON + HTTP API for languages that cannot import a Go library.

Features

  • Go library with a two-call public API: Check and Apply.
  • Non-intrusive — no daemon, service, UI toolkit, event loop or app lifecycle integration.
  • Windows, macOS and Linux, amd64 and arm64, using one CGO-free binary for the optional sidecar.
  • GitHub Releases or self-hosted static manifests, with an ordered source chain and same-version/SHA-256 mirror failover.
  • SHA-256 for every artifact, optional Ed25519 detached signatures, TLS enforcement and host allowlists.
  • Transactional directory/file installs with backups, crash recovery and rollback data.
  • Safe archive extraction: traversal, symlink escape, hardlink, special-file, entry-count and expansion-size defenses.
  • Resumable downloads with bounded retries, explicit proxy support and .part files.
  • Deferred apply for Windows when the application is still running, plus optional macOS signer continuity, quarantine removal and ad-hoc signing.
  • Release tooling: archive, hash, sign, manifest and checksum generation.

Install

go get github.com/yxxbc/wmlUpdate
import wmlupdate "github.com/yxxbc/wmlUpdate"

For the CLI, download a verified release archive or build it from source:

go install github.com/yxxbc/wmlUpdate/cmd/wml-update@latest

Release checksums are signed with the project's Ed25519 key. Verify them with signing/release-signing-key.pub.pem; see signing/README.md.

Configuration

The library accepts either a complete updater.json or a programmatic wmlupdate.Config. A minimal GitHub configuration:

{
  "schema_version": 1,
  "app_id": "com.example.myapp",
  "channel": "stable",
  "source": {
    "type": "github",
    "owner": "acme",
    "repo": "myapp",
    "tag_prefix": "v",
    "token_env": "GITHUB_TOKEN"
  },
  "asset_rules": [
    {"os": "darwin", "arch": "arm64", "match": "myapp-{version}-macos-arm64.zip", "format": "zip"},
    {"os": "windows", "arch": "amd64", "match": "myapp-{version}-windows-amd64.zip", "format": "zip"},
    {"os": "linux", "arch": "amd64", "match": "myapp-{version}-linux-amd64.tar.gz", "format": "tar.gz"}
  ],
  "install": {
    "type": "directory",
    "paths": {
      "darwin": "/Applications/MyApp.app",
      "windows": "C:\\Program Files\\MyApp",
      "linux": "/opt/myapp"
    },
    "preserve": ["data/**", "config/**"]
  }
}

Or configure entirely in Go:

u, err := wmlupdate.New(wmlupdate.Options{
    CurrentVersion: appVersion,
    Config: &wmlupdate.Config{
        AppID: "com.example.myapp",
        Source: wmlupdate.SourceConfig{
            Type: wmlupdate.SourceGitHub, Owner: "acme", Repo: "myapp",
        },
        Install: wmlupdate.InstallConfig{
            Type: wmlupdate.InstallDirectory, Path: "/opt/myapp",
        },
    },
})

See the SDK guide and configuration reference.

Integrate in Go

u, err := wmlupdate.New(wmlupdate.Options{
    ConfigPath:     "updater.json",
    CurrentVersion: appVersion,
    Events: func(e wmlupdate.Event) {
        if e.Type == wmlupdate.EventProgress {
            reportProgress(e.Percent)
        }
    },
})
if err != nil { return err }
defer u.Close()

update, err := u.Check(ctx)
if err != nil { return err }
if !update.Available {
    return nil
}

result, err := u.Apply(ctx, update, wmlupdate.ApplyOptions{})
if err != nil { return err }
log.Printf("updated %s -> %s", result.PreviousVersion, result.Version)

On Windows, when the target is running:

result, err := u.Apply(ctx, update, wmlupdate.ApplyOptions{
    Defer:    true,
    WaitPIDs: []int{os.Getpid()},
})
// StatusApplyScheduled: exit the app and let the detached helper finish.

The library does not call os.Exit or restart your process. That remains your application's decision.

Packaging and releases

The CLI includes a release workflow for GitHub Releases or any static host:

wml-update keygen --out-dir keys
wml-update publish --spec publish.json --out public/stable --key keys/signing-key.pem

publish.json:

{
  "app_id": "com.example.myapp",
  "version": "1.2.0",
  "channel": "stable",
  "notes": "Fixes and improvements",
  "assets": [
    {"dir": "dist/app-linux", "url": "myapp-1.2.0-linux-amd64.tar.gz", "os": "linux", "arch": "amd64", "format": "tar.gz"},
    {"dir": "dist/app-windows", "url": "myapp-1.2.0-windows-amd64.zip", "os": "windows", "arch": "amd64", "format": "zip"}
  ]
}

Outputs include manifest.json, manifest.json.sig, checksums.txt and checksums.txt.sig. Upload the assets first, then the manifest. See self-hosting and GitHub Releases.

Other languages

A Go library cannot be imported directly by Python, Node.js, Java or C#. Those applications use the optional sidecar adapter:

wml-update --config updater.json --json update --current-version 1.0.0

stdout is one JSON object per line; stderr carries diagnostics. The same engine is also available through:

wml-update --config updater.json serve --json

which provides a token-authenticated loopback HTTP API with jobs and SSE. See integration and examples for Python, Node.js, C#, shell, the Go SDK example and the Go CLI example.

CLI commands

The CLI is optional for Go applications. It contains the release tool and the sidecar adapter.

Command Purpose
publish Build self-hosted manifest, checksums and signatures
keygen / sign / verify Ed25519 release-signing tools
check / update Sidecar check and apply; --json for NDJSON
update --defer --wait-pid <pid> Deferred apply for a running target
fetch --output app.zip Verified download only
rollback / status / doctor Operational recovery and diagnostics
serve Local HTTP integration API

Security defaults

  • Downloads without a SHA-256 are refused. GitHub's API digest or a checksums.txt asset supplies the expected hash.
  • With security.public_key_file configured, self-hosted manifests must have a valid detached signature, and GitHub releases must have signed checksums.
  • HTTPS is required; plain HTTP is accepted only for loopback or with an explicit security.allow_insecure_http opt-in.
  • Redirect targets are re-checked against the scheme, optional host allowlist and redirect count.
  • Archives reject absolute paths, traversal, escaping symlinks, hardlinks, device files and excessive expansion.
  • Directory installs are staged and swapped transactionally. The previous installation is retained for rollback; a journal repairs interrupted swaps.
  • The HTTP adapter requires a token and binds loopback unless --allow-remote is explicitly set.

Read the security model. The directory strategy manages the target directory as a unit: files not present in the new release and not matched by install.preserve are removed. Point it at an application-managed directory, not a user home directory.

Documentation

Online documentation: https://yxxbc.github.io/wmlUpdate/

Development

Requires Go 1.24+.

make test
make check
make build

Cross-compilation is CGO-free:

GOOS=windows GOARCH=amd64 go build ./cmd/wml-update
GOOS=darwin  GOARCH=arm64 go build ./cmd/wml-update
GOOS=linux   GOARCH=amd64 go build ./cmd/wml-update

License

Licensed under the Apache License, Version 2.0. See NOTICE for attribution. The WML Update name and logo are project marks; the Apache-2.0 license does not grant trademark rights or permission to imply endorsement.

Documentation

Overview

Package wmlupdate is a lightweight, embeddable updater library.

The public surface is deliberately small:

u, err := wmlupdate.New(wmlupdate.Options{
    ConfigPath:     "updater.json",
    CurrentVersion: currentVersion,
})
update, err := u.Check(ctx)
if update.Available {
    result, err := u.Apply(ctx, update, wmlupdate.ApplyOptions{})
}

The library owns release discovery, verified download, installation, backup and rollback bookkeeping. It does not start a daemon, require a specific application framework, or control the host process lifecycle. For languages without a native binding, the command in cmd/wml-update exposes the same engine over NDJSON and an optional loopback HTTP API.

Index

Constants

View Source
const (
	SourceGitHub = "github"
	SourceStatic = "static"
	SourceChain  = "chain"
)

Source type values.

View Source
const (
	InstallDirectory = "directory"
	InstallFile      = "file"
	InstallCommand   = "command"
	InstallExternal  = "external"
)

Install type values.

View Source
const (
	StatusUpdated        = "updated"
	StatusDownloaded     = "downloaded"
	StatusApplyScheduled = "apply_scheduled"
)

Apply status values.

Variables

View Source
var ErrNoUpdate = errors.New("no update available")

ErrNoUpdate is returned when Apply is called without an available update.

Functions

func Code

func Code(err error) string

Code returns the stable error code for err, or "internal" for unknown errors.

Types

type ApplyOptions

type ApplyOptions struct {
	// Defer stages the update and applies it from a detached helper after the
	// selected processes exit. Recommended on Windows when the target is
	// running, and required when replacing the running updater itself.
	Defer bool `json:"defer,omitempty"`
	// WaitPIDs lists processes that must exit before a deferred apply.
	WaitPIDs []int `json:"wait_pids,omitempty"`
}

ApplyOptions controls installation.

type Asset

type Asset struct {
	Name   string `json:"name"`
	OS     string `json:"os"`
	Arch   string `json:"arch"`
	Format string `json:"format,omitempty"`
	SHA256 string `json:"sha256,omitempty"`
	Size   int64  `json:"size,omitempty"`
}

Asset describes the selected platform artifact.

type AssetRule

type AssetRule struct {
	OS     string `json:"os,omitempty"`
	Arch   string `json:"arch,omitempty"`
	Match  string `json:"match,omitempty"`
	Format string `json:"format,omitempty"`
}

AssetRule selects a release asset for a platform. Rules are evaluated in order. Match supports {version}, {tag}, {os}, {arch} and glob wildcards.

type Config

type Config struct {
	AppID          string         `json:"app_id"`
	Channel        string         `json:"channel,omitempty"`
	CurrentVersion string         `json:"current_version,omitempty"`
	Source         SourceConfig   `json:"source"`
	AssetRules     []AssetRule    `json:"asset_rules,omitempty"`
	Install        InstallConfig  `json:"install"`
	StateDir       string         `json:"state_dir,omitempty"`
	Security       SecurityConfig `json:"security,omitempty"`
	Download       DownloadConfig `json:"download,omitempty"`
}

Config is the programmatic form of the updater configuration. It mirrors the updater.json schema but is intentionally limited to the fields an embedding application needs. Use Options.ConfigPath when you want to ship a JSON file with environment expansion and schema support.

type DownloadConfig

type DownloadConfig struct {
	MaxBytes int64         `json:"max_bytes,omitempty"`
	Timeout  time.Duration `json:"timeout,omitempty"`
	Retries  int           `json:"retries,omitempty"`
	Proxy    string        `json:"proxy,omitempty"`
}

DownloadConfig bounds downloads and retries.

type Error

type Error struct {
	Code string
	Err  error
}

Error is a stable, machine-readable SDK error.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Event

type Event struct {
	Type    EventType `json:"type"`
	Time    time.Time `json:"time"`
	Phase   string    `json:"phase,omitempty"`
	Message string    `json:"message,omitempty"`
	Bytes   int64     `json:"bytes,omitempty"`
	Total   int64     `json:"total,omitempty"`
	Percent float64   `json:"percent,omitempty"`
}

Event is delivered to Options.Events. It is a read-only progress/log signal; the terminal result is returned by Check and Apply.

type EventFunc

type EventFunc func(Event)

EventFunc receives progress and log callbacks. It is called synchronously.

type EventType

type EventType string

EventType identifies a callback event.

const (
	EventLog      EventType = "log"
	EventWarning  EventType = "warning"
	EventProgress EventType = "progress"
)

type InstallConfig

type InstallConfig struct {
	Type            string            `json:"type"`
	Path            string            `json:"path,omitempty"`
	Paths           map[string]string `json:"paths,omitempty"`
	StripComponents int               `json:"strip_components,omitempty"`
	Preserve        []string          `json:"preserve,omitempty"`
	FileMode        string            `json:"file_mode,omitempty"`
	KeepBackups     int               `json:"keep_backups,omitempty"`

	Command string            `json:"command,omitempty"`
	Args    []string          `json:"args,omitempty"`
	WorkDir string            `json:"workdir,omitempty"`
	Env     map[string]string `json:"env,omitempty"`
	Timeout time.Duration     `json:"timeout,omitempty"`

	RelaunchPath string   `json:"relaunch_path,omitempty"`
	RelaunchArgs []string `json:"relaunch_args,omitempty"`

	MacOS MacOSConfig `json:"macos,omitempty"`
}

InstallConfig controls how a verified artifact is applied.

type MacOSConfig added in v1.0.0

type MacOSConfig struct {
	// RemoveQuarantine removes com.apple.quarantine recursively. This bypasses
	// Gatekeeper's first-launch check; enable only for verified updates.
	RemoveQuarantine bool `json:"remove_quarantine,omitempty"`
	// AdHocSign force-signs the target with an ad-hoc signature. This can make
	// unsigned bundles launch, but destroys Developer ID signatures and
	// notarization; use only for unsigned distributions.
	AdHocSign bool `json:"ad_hoc_sign,omitempty"`
	// AllowSignerChange permits a Developer ID team change. By default an
	// installed signed .app requires the downloaded app to have the same team.
	AllowSignerChange bool `json:"allow_signer_change,omitempty"`
}

MacOSConfig controls optional post-install metadata handling on macOS.

type Options

type Options struct {
	// ConfigPath loads a complete updater.json configuration, including
	// environment references and $schema support.
	ConfigPath string
	// Config supplies a programmatic configuration. Set exactly one of
	// ConfigPath and Config.
	Config *Config
	// BaseDir resolves relative paths in a programmatic Config. It defaults to
	// the current working directory.
	BaseDir string
	// CurrentVersion is the running application version. When empty, the
	// library falls back to the recorded state and then Config.CurrentVersion.
	CurrentVersion string
	// Platform overrides the target platform for cross-platform packaging tests.
	Platform *Platform
	// Events receives progress, log and warning callbacks. It is invoked
	// synchronously and should return quickly.
	Events EventFunc
}

Options configures an Updater.

type Platform

type Platform struct {
	OS   string `json:"os"`
	Arch string `json:"arch"`
}

Platform overrides the target platform. The zero value means the host platform.

type Result

type Result struct {
	Status          string `json:"status"`
	PreviousVersion string `json:"previous_version,omitempty"`
	Version         string `json:"version,omitempty"`
	ArtifactPath    string `json:"artifact_path,omitempty"`
	PlanID          string `json:"plan_id,omitempty"`
	HelperPID       int    `json:"helper_pid,omitempty"`
}

Result describes a completed Apply.

type SecurityConfig

type SecurityConfig struct {
	PublicKey               string   `json:"public_key,omitempty"`
	PublicKeyFile           string   `json:"public_key_file,omitempty"`
	ChecksumsAsset          string   `json:"checksums_asset,omitempty"`
	ChecksumsSignatureAsset string   `json:"checksums_signature_asset,omitempty"`
	AllowedHosts            []string `json:"allowed_hosts,omitempty"`
	AllowInsecureHTTP       bool     `json:"allow_insecure_http,omitempty"`
	AllowUnverifiedAssets   bool     `json:"allow_unverified_assets,omitempty"`
}

SecurityConfig controls signature verification and network boundaries.

type SourceConfig

type SourceConfig struct {
	Type string `json:"type"`

	// Chain tries sources in order until one returns valid metadata.
	Sources []SourceConfig `json:"sources,omitempty"`

	// GitHub Releases.
	Owner             string `json:"owner,omitempty"`
	Repo              string `json:"repo,omitempty"`
	APIBase           string `json:"api_base,omitempty"`
	TagPrefix         string `json:"tag_prefix,omitempty"`
	TokenEnv          string `json:"token_env,omitempty"`
	IncludePrerelease bool   `json:"include_prerelease,omitempty"`

	// Self-hosted static manifest.
	ManifestURL  string            `json:"manifest_url,omitempty"`
	ManifestURLs []string          `json:"manifest_urls,omitempty"`
	Headers      map[string]string `json:"headers,omitempty"`

	// AssetMirrors are URL templates for download failover.
	AssetMirrors []string `json:"asset_mirrors,omitempty"`
}

SourceConfig describes GitHub Releases or a self-hosted manifest.

type Update

type Update struct {
	Available      bool      `json:"available"`
	CurrentVersion string    `json:"current_version"`
	Version        string    `json:"version"`
	Channel        string    `json:"channel,omitempty"`
	Source         string    `json:"source,omitempty"`
	Tag            string    `json:"tag,omitempty"`
	Notes          string    `json:"notes,omitempty"`
	PublishedAt    time.Time `json:"published_at,omitempty"`
	Mandatory      bool      `json:"mandatory,omitempty"`
	Prerelease     bool      `json:"prerelease,omitempty"`
	Asset          Asset     `json:"asset"`
	// contains filtered or unexported fields
}

Update is the result of Check. Its exported fields are informational. Apply uses an opaque checked snapshot retained by the Updater; do not construct or serialize Update values.

type Updater

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

Updater is a configured, embeddable updater. Check may be called concurrently; Apply takes an inter-process lock and is serialized.

func New

func New(opts Options) (*Updater, error)

New constructs an Updater.

func (*Updater) Apply

func (u *Updater) Apply(ctx context.Context, update *Update, opts ApplyOptions) (*Result, error)

Apply installs a release returned by Check. The exact checked release is retained internally, so source metadata is not re-fetched; artifact bytes are still downloaded and verified against the checked SHA-256 before use.

func (*Updater) Check

func (u *Updater) Check(ctx context.Context) (*Update, error)

Check discovers the latest eligible release without downloading it.

func (*Updater) Close

func (u *Updater) Close()

Close releases idle network connections. It is safe to call more than once.

Directories

Path Synopsis
cmd
wml-update command
Command wml-update is a cross-platform updater for GitHub Releases and self-hosted static manifests.
Command wml-update is a cross-platform updater for GitHub Releases and self-hosted static manifests.
examples
go command
Command example demonstrates embedding the updater library.
Command example demonstrates embedding the updater library.
go-cli command
Command example runs wml-update and consumes its NDJSON event stream.
Command example runs wml-update and consumes its NDJSON event stream.
playground command
Command playground runs a complete local update without any external service.
Command playground runs a complete local update without any external service.
internal
api
Package api implements the optional local integration API.
Package api implements the optional local integration API.
archive
Package archive safely extracts and creates ZIP and tar archives.
Package archive safely extracts and creates ZIP and tar archives.
buildinfo
Package buildinfo holds version metadata injected at build time.
Package buildinfo holds version metadata injected at build time.
checksum
Package checksum parses and computes SHA-256 digests.
Package checksum parses and computes SHA-256 digests.
cli
config
Package config loads and validates updater configuration.
Package config loads and validates updater configuration.
engine
Package engine orchestrates checks, verified downloads, installation, rollback and the JSON-facing result model.
Package engine orchestrates checks, verified downloads, installation, rollback and the JSON-facing result model.
event
Package event defines the stable event stream shared by the CLI JSON mode and the local HTTP API.
Package event defines the stable event stream shared by the CLI JSON mode and the local HTTP API.
httpx
Package httpx provides a hardened HTTP client, bounded retries and resumable downloads for the updater.
Package httpx provides a hardened HTTP client, bounded retries and resumable downloads for the updater.
install
Package install applies verified artifacts transactionally.
Package install applies verified artifacts transactionally.
lock
Package lock serializes operations that mutate an installation.
Package lock serializes operations that mutate an installation.
manifest
Package manifest defines the self-hosted update manifest format and its detached Ed25519 signature.
Package manifest defines the self-hosted update manifest format and its detached Ed25519 signature.
platform
Package platform normalizes operating system and architecture names and selects platform-specific assets.
Package platform normalizes operating system and architecture names and selects platform-specific assets.
publish
Package publish builds self-hosted manifests, signed checksum files and release archives from a publish specification.
Package publish builds self-hosted manifests, signed checksum files and release archives from a publish specification.
release
Package release contains the normalized release model shared by update sources and the update engine.
Package release contains the normalized release model shared by update sources and the update engine.
sign
Package sign implements Ed25519 key generation, detached signatures and public-key loading for update manifests and checksum files.
Package sign implements Ed25519 key generation, detached signatures and public-key loading for update manifests and checksum files.
source
Package source fetches normalized release metadata from GitHub Releases or a self-hosted static manifest.
Package source fetches normalized release metadata from GitHub Releases or a self-hosted static manifest.
state
Package state persists updater bookkeeping: installed version, update history, backups and an in-flight install journal.
Package state persists updater bookkeeping: installed version, update history, backups and an in-flight install journal.
version
Package version implements strict Semantic Versioning 2.0.0 parsing and ordering.
Package version implements strict Semantic Versioning 2.0.0 parsing and ordering.

Jump to

Keyboard shortcuts

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