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:
- 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.
- 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.
- 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:
tracingroutes diagnostics throughtracinginstead oflog, adding#[instrument]spans and structured events for each step of the update.opentelemetry(which impliestracing) carries the active OpenTelemetry trace context inside the serializedUpdateState— 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§
- Default
Launcher - The default
Launcher: relaunches with the library’sRESUME_FLAGand spawns a detached child process. Used unlessUpdateManager::with_launcherinstalls a different one. - Error
- The fundamental error type used by this library.
- GitHub
Source - A
Sourcewhich 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. - Release
Variant - A downloadable binary belonging to a
Release— a single release asset. - Update
Manager - Drives the three-phase, in-place self-update of an application binary.
- Update
State - The serializable state which is threaded through the three phases of an
update by relaunching the application with the
RESUME_FLAGfollowed by this value as JSON.
Enums§
- Update
Phase - The phase of the three-phase update process which an
UpdateStateis 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 bynaming::rustto 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
UpdateManagercan list and download binaries from.