Skip to content

update

mytool update [check | run [OPTIONS]]

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 0check 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

  1. Preflight — release source and public keys present.
  2. Resolve the release: release_by_tag("v<target>") or latest_release().
  3. Parse the tag as semver. Stop with already at <v> if it equals the running version and --force was not given.
  4. Render the expected asset name from update_asset_pattern and pick the matching asset.
  5. Find the detached signature: <asset>.minisig, else <asset>.sig.
  6. Stream the asset into the cache directory, emitting Downloading events.
  7. Verify the minisign signature. Then, only if update_checksums_asset is set, fetch that asset and verify the SHA-256.
  8. Extract the binary from the archive into <cache>/bin/.
  9. Self-test the staged binary, then mark it executable.
  10. 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 itupdate 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.