1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! 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](https://github.com/SierraSoftworks/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*](https://sierrasoftworks.com/2019/10/15/app-updates/#cleanup).
//!
//! # 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.
//!
//! ```no_run
//! 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`](DefaultLauncher::with_arg) /
//! [`with_env`](DefaultLauncher::with_env) builders, so the common cases need no
//! custom launcher. For more control, install any [`Launcher`] with
//! [`with_launcher`](UpdateManager::with_launcher): override
//! [`resume_args`](Launcher::resume_args) to hand the state to a sub-command, or
//! [`launch`](Launcher::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`](https://docs.rs/log) facade, which does nothing until the application
//! installs a logger. Two opt-in features build on that:
//!
//! - **`tracing`** routes diagnostics through
//! [`tracing`](https://docs.rs/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`](UpdateManager::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.
pub use ;
pub use Error;
pub use UpdateManager;
pub use ;
/// Re-export of the [`reqwest`] version this crate is built against, so callers
/// can construct a [`reqwest::Client`] of the exact type
/// [`GitHubSource::with_client`] expects.
pub use reqwest;
pub use ;
pub use ;
/// The command-line flag the library uses to relaunch the consuming binary
/// between update phases.
///
/// A consuming `main()` **must** detect this flag (before any other argument
/// parsing), pass the JSON value that follows it to
/// [`UpdateManager::resume_from_arg`], and exit immediately if it returns
/// `Ok(true)` — so that the next phase's process can take ownership of the
/// binary.
pub const RESUME_FLAG: &str = "--update-resume-internal";
/// 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.
pub const TARGET: &str = env!;