Skip to main content

Crate update_rs

Crate update_rs 

Source
Expand description

Self-contained, in-place self-updates for Rust applications.

update-rs lets a Rust application replace its own binary on disk with a newer release and relaunch into it — without an installer, package manager, or external updater process. It was extracted from the Sierra Softworks Git-Tool project, where it powers the gt update command.

§The three-phase update

A running executable can’t reliably overwrite itself (Windows holds an exclusive lock on a running image), so the update is performed across three phases, each running from a different binary and relaunching the next:

  1. Prepare — the running application downloads the new release to a temporary file next to it, marks it executable (on Unix), and launches that temporary binary to perform the next phase.
  2. Replace — the temporary binary deletes the original application file and copies itself over it (retrying while the old process exits), then launches the freshly replaced original.
  3. Cleanup — the updated original deletes the leftover temporary binary and returns control to the application.

The phases are threaded together by relaunching the binary with RESUME_FLAG followed by a serialized UpdateState. This design is described in more detail in Building self-updating applications.

§Quick start

Build an UpdateManager around a Source (the crate ships GitHubSource), detect the RESUME_FLAG before any other argument parsing, and otherwise offer the newest release. The asset to download is chosen by a glob pattern; the naming helpers build one for the current platform.

use update_rs::{naming, GitHubSource, Release, UpdateManager, RESUME_FLAG};

#[tokio::main]
async fn main() -> Result<(), update_rs::Error> {
    let manager = UpdateManager::new(
        // e.g. matches "yourapp-linux-amd64", "yourapp-windows-amd64.exe", ...
        GitHubSource::new("yourorg/yourapp", naming::go("yourapp"))
            .with_release_tag_prefix("v"), // strips the leading v in vX.Y.Z tags
    );

    // The updater relaunches your application between phases, passing the
    // serialized update state after `RESUME_FLAG`. Detect it first and hand
    // control back to the library.
    let args: Vec<String> = std::env::args().collect();
    if let Some(i) = args.iter().position(|a| a == RESUME_FLAG) {
        if manager.resume_from_arg(&args[i + 1]).await? {
            return Ok(()); // a phase was launched; exit so it can take over
        }
    }

    // Otherwise, look for the newest release with a binary for this platform.
    let releases = manager.get_releases().await?;
    let latest = Release::get_latest(releases.iter().filter(|r| r.get_variant().is_some()));
    if let Some(latest) = latest {
        if manager.update(latest).await? {
            println!("Shutting down to complete the update.");
            return Ok(()); // exit promptly so the new binary can take over
        }
    }

    // ... your normal application logic ...
    Ok(())
}

§Selecting the release asset

GitHubSource::new takes a glob pattern (* and ? wildcards) that is matched against each release’s asset file names, so your project can name its assets however it likes. Build the pattern by hand — format!("yourapp-{OS}-{ARCH}{EXE_SUFFIX}") using std::env::consts — or with a naming helper: naming::go for Go-style names (yourapp-linux-amd64) or naming::rust for the Rust target triple (yourapp-x86_64-unknown-linux-gnu).

§Customising the relaunch

The updater relaunches your binary between phases through a Launcher. The default, DefaultLauncher, passes the RESUME_FLAG and serialized state and spawns a detached child — and can thread your own arguments and environment variables through to the relaunched process via its with_arg / with_env builders, so the common cases need no custom launcher. For more control, install any Launcher with with_launcher: override resume_args to hand the state to a sub-command, or launch for the whole command. (The opentelemetry feature below already propagates the trace context for you, so that case needs no wiring.)

§Observability

By default the crate emits its diagnostic events through the lightweight log facade, which does nothing until the application installs a logger. Two opt-in features build on that:

  • tracing routes diagnostics through tracing instead of log, adding #[instrument] spans and structured events for each step of the update.
  • opentelemetry (which implies tracing) carries the active OpenTelemetry trace context inside the serialized UpdateState — not as an extra command-line argument — so the three phases, which each run in a separate process, stitch together into one distributed trace. It reads and writes only the global propagator (opentelemetry::global::get_text_map_propagator), so the host application stays in full control of how (and whether) traces are exported; if no propagator is installed, the feature is a no-op.

There is nothing extra to wire up: detect RESUME_FLAG and call resume_from_arg as usual, and the trace context rides along with the update state automatically.

§Windows: avoiding UAC / Error 740

Because the temporary binary is named like yourapp-<tag>.exe, Windows’ installer-detection heuristic can decide it’s an installer and demand UAC elevation — which fails the relaunch with ERROR_ELEVATION_REQUIRED (Win32 error 740). The fix is to ship your binary with an asInvoker application manifest. This is a property of the consuming binary (a library can’t embed a manifest), so update-rs ships a ready-to-copy build.rs and manifest template under examples/windows-manifest/; see the project README for details.

Re-exports§

pub use reqwest;

Modules§

naming
Helpers for building release-asset name patterns for the current platform.

Structs§

DefaultLauncher
The default Launcher: relaunches with the library’s RESUME_FLAG and spawns a detached child process. Used unless UpdateManager::with_launcher installs a different one.
Error
The fundamental error type used by this library.
GitHubSource
A Source which lists and downloads releases from a GitHub repository’s releases API.
Release
A single release published by a Source, along with the binary (variant) selected for it by the source’s configured asset selection.
ReleaseVariant
A downloadable binary belonging to a Release — a single release asset.
UpdateManager
Drives the three-phase, in-place self-update of an application binary.
UpdateState
The serializable state which is threaded through the three phases of an update by relaunching the application with the RESUME_FLAG followed by this value as JSON.

Enums§

UpdatePhase
The phase of the three-phase update process which an UpdateState is in.

Constants§

RESUME_FLAG
The command-line flag the library uses to relaunch the consuming binary between update phases.
TARGET
The Rust target triple this crate was compiled for (e.g. x86_64-unknown-linux-gnu), captured at build time. Used by naming::rust to build release asset names.

Traits§

Launcher
Launches the application binary to drive the next phase of an update.
Source
A source of application releases which the UpdateManager can list and download binaries from.