update¶
Feature: Update, registered by the rtb-update crate. Passthrough subtree.
No subcommand means check.
update rejects --output. Its inner parser does not strip the global flag,
so mytool update check --output json fails with unexpected argument '--output'.
What must be configured before either subcommand works¶
ToolMetadata field |
Needed for | Failure when missing |
|---|---|---|
release_source |
both | update: no `release_source` configured on ToolMetadata |
update_public_keys |
run |
rtb::update::no_public_key |
check only needs a release source. run performs its preflight before touching
the network and refuses immediately if the trust set is empty — a tool that cannot
verify a signature does not download one.
release_credential is optional. When set, the token is resolved through the
credential precedence chain and passed to the provider; a NotFound result is
treated as "run unauthenticated", while any other resolution error fails the
command.
update check¶
Fetches release metadata and compares versions. Downloads nothing else.
| Situation | Output |
|---|---|
| Same version | up to date — running version <v> |
| Newer upstream | new version available: <current> -> <latest> plus a hint to run update run |
| Upstream is older | running newer than the upstream report: current <c> > latest <l> (likely tool-author misconfiguration) |
All three exit 0 — check reports, it does not gate. A release tag that is not
semver (after stripping a leading v or V) fails with
release tag `<tag>` is not a semver.
update run¶
| Flag | Default | Meaning |
|---|---|---|
--target <VERSION> |
latest | Pin to a semver version, without a v prefix. The tag looked up is v<VERSION> |
--force |
off | Re-install even when already current, and skip the downgrade refusal |
--include-prereleases |
off | Currently has no effect — see below |
--dry-run |
off | Verify and stage, but do not swap. Prints where the staged binary is |
--progress |
off | Print progress events to stderr as the flow runs |
Output on success:
updated: 0.4.1 -> 0.5.0 # swapped
dry run: staged 0.4.1 -> 0.5.0 at /path/to/staged/mytool # --dry-run
already at 0.4.1 # nothing to do
--include-prereleases is accepted and ignored¶
The flag parses, and it reaches RunOptions::include_prereleases, but nothing in
the update flow reads that field. "Latest" is whatever the release provider's
latest_release() returns, with or without the flag. To install a prerelease, name
it explicitly with --target.
--target requires the tag to be exactly v<VERSION>¶
The tag is built as format!("v{version}"). A project that tags releases as
1.2.3, release-1.2.3 or mytool-v1.2.3 cannot be targeted this way. Of those
three, only 1.2.3 is parsed happily by plain check — it strips a leading v or
V and then requires strict semver, so release-1.2.3 and mytool-v1.2.3 fail
check too, with release tag `<tag>` is not a semver.
Downgrades are refused unless forced¶
--target below the running version fails with
downgrade refused: target <t> is older than current <c> and diagnostic code
rtb::update::downgrade_refused. --force bypasses the check.
The refusal only applies to --target. It is a guard against a mistyped version,
not a policy against downgrading.
What a run actually does¶
- Preflight — release source and public keys present.
- Resolve the release:
release_by_tag("v<target>")orlatest_release(). - Parse the tag as semver. Stop with
already at <v>if it equals the running version and--forcewas not given. - Render the expected asset name from
update_asset_patternand pick the matching asset. - Find the detached signature:
<asset>.minisig, else<asset>.sig. - Stream the asset into the cache directory, emitting
Downloadingevents. - Verify the minisign signature. Then, only if
update_checksums_assetis set, fetch that asset and verify the SHA-256. - Extract the binary from the archive into
<cache>/bin/. - Self-test the staged binary, then mark it executable.
- Swap it in via
self-replace— unless--dry-run, which stops here and reports the staged path.
A failure at any step leaves the running binary untouched. The only step that mutates the installed binary is the last one.
Asset naming¶
The default pattern is {name}-{version}-{target}{ext}, overridable via
ToolMetadata::update_asset_pattern.
| Placeholder | Value |
|---|---|
{name} |
ToolMetadata::name |
{version} |
Release tag with a leading v stripped |
{target} |
Rust host triple, e.g. x86_64-unknown-linux-gnu |
{os} |
linux, macos, windows |
{arch} |
x86_64, aarch64 |
{ext} |
.tar.gz everywhere except Windows, which gets .zip |
Only six host combinations produce a {target}: Linux, macOS and Windows on
x86_64 and aarch64. Anything else renders {target} as an empty string, which
is the signal to set an explicit pattern. Unrecognised placeholders are left
verbatim.
Selection tries an exact name match first, then a case-insensitive prefix match up
to the archive extension, which lets a published .tgz satisfy a pattern ending
.tar.gz. No match at all is rtb::update::no_matching_asset.
Archive formats¶
.tar.gz, .tgz and .zip only. Any other extension fails with
unsupported archive extension: <name>. The extracted tree is searched for a file
named exactly <tool name> or <tool name>.exe; if the archive nests the binary
under a different name, extraction succeeds and the search fails with
extracted archive contained no `<tool>` binary.
The staged binary self-test¶
Before swapping, the staged binary is executed as <staged> --version. The run is
refused unless the process exits 0 and its stdout contains the release tag
(with or without the leading v).
This is where a tool built straight on Application::builder will come unstuck:
rtb-cli does not declare a root --version flag, so the staged binary exits
non-zero and every update fails with rtb::update::self_test_failed. A tool that
enables self-update needs a root --version that prints its version — see
the root flags.
There is also no timeout on the self-test. A staged binary that hangs on
--version hangs the update.
Where files are staged¶
<cache_dir>/update/<tag>/, with <cache_dir> from
directories::ProjectDirs::from("", "", <tool name>).cache_dir():
| Platform | Path for mytool, tag v1.2.3 |
|---|---|
| Linux | ~/.cache/mytool/update/v1.2.3/ |
| macOS | ~/Library/Caches/mytool/update/v1.2.3/ |
| Windows | %LOCALAPPDATA%\mytool\cache\update\v1.2.3\ |
If no cache directory can be resolved, the system temp directory is used instead.
Note the qualifier here is empty, where the config-directory lookup used dev —
on macOS that means the cache lives under mytool while config lives under
dev.mytool.
Nothing cleans this directory up. A tool that updates often accumulates one archive plus one extracted binary per release.
Failure codes¶
| Diagnostic code | Cause |
|---|---|
rtb::update::no_source |
release_source not set |
rtb::update::no_public_key |
update_public_keys empty |
rtb::update::malformed_public_key |
Every trusted key failed to parse |
rtb::update::no_matching_asset |
No release asset matched the host |
rtb::update::missing_signature |
Neither <asset>.minisig nor <asset>.sig present |
rtb::update::bad_signature |
Signature malformed, legacy Ed, wrong key id, or did not verify |
rtb::update::bad_checksum |
SHA-256 mismatch, or the named checksums asset is absent |
rtb::update::self_test_failed |
Staged binary failed --version or did not report the expected tag |
rtb::update::downgrade_refused |
--target older than current, without --force |
rtb::update::archive |
Unsupported archive, extraction error, or no binary inside |
rtb::update::pattern |
Release tag was not semver |
rtb::update::swap_failed |
self-replace could not replace the running binary |
rtb::update::io |
Filesystem error while staging |
Offline updates are library-only¶
Updater::run_from_file(asset, signature, options) verifies and swaps from a
locally staged archive with no provider access. There is no CLI flag for it —
update run always talks to the release source. A tool that needs an airgap path
has to call the library API from a command of its own.
run_from_file also skips two things run does: there is no checksum verification
even when update_checksums_asset is set, and there is no downgrade check. The
version it reports is whatever the staged binary's --version output parses to.