Ship self-update¶
Four things have to line up: the metadata, the published assets, the trusted keys,
and a --version flag on your binary. Getting three of four gives you a tool that
downloads an update and refuses to install it.
Set the release source¶
use rtb_app::metadata::ReleaseSource;
ToolMetadata::builder()
.name("mytool")
.summary("does the thing")
.release_source(ReleaseSource::Gitlab {
project: "myorg/group/mytool".into(),
host: "gitlab.com".into(),
})
.update_public_keys(vec![
"RW…".to_string(), // base64 minisign public key
])
.build()
Without release_source, both update check and update run fail immediately.
Without update_public_keys, update run refuses before touching the network —
a tool that cannot verify a signature does not download one.
For a private release source, set release_credential as well; the token is
resolved through the credential chain at update time, and a NotFound result just
means the provider runs unauthenticated.
Publish assets the selector can find¶
The default asset-name pattern is {name}-{version}-{target}{ext}, so a v1.2.3
release for x86-64 Linux must be published as:
If your pipeline names things differently, set update_asset_pattern rather than
renaming your releases. Placeholders are {name}, {version}, {target}, {os},
{arch} and {ext}; unknown ones pass through verbatim.
Three constraints on the archive itself:
- Format must be
.tar.gz,.tgzor.zip. Anything else fails extraction. - The binary inside must be named exactly
<tool name>(or<tool name>.exe). Nesting it in a directory is fine; renaming it is not. - The signature must be
<asset>.minisig—<asset>.sigis also accepted, but.minisigis preferred and matches what minisign produces.
Optionally publish a sha256sum-format checksums file and name it in
update_checksums_asset. Without that field set, no checksum is verified at
all; the signature is the only integrity gate.
Sign with the prehashed variant¶
Only minisign's prehashed ED signatures are accepted. A legacy pure Ed
signature is refused even when the trusted key could verify it, because
cargo-binstall rejects legacy too and one published signature has to satisfy both
installers. Modern minisign produces prehashed signatures by default.
Pin the same base64 public key string in both places — update_public_keys here and
pubkey under [package.metadata.binstall.signing] — so the two installers cannot
end up trusting different things.
Ship two keys during a rotation. Any one verifying is enough, so a release trusting
{old, new} spans the change with no dual-signing window.
Give your binary a root --version flag¶
This is the step that catches people. Before swapping, update run executes
<staged-binary> --version and requires exit 0 with the release tag in
stdout.
rtb-cli does not declare a root --version flag. A tool built straight on
Application::builder answers mytool --version with command not found: --version
and a non-zero exit, so its own self-test fails and every update stops with
rtb::update::self_test_failed.
Make --version work before you enable self-update, and check it by hand:
There is also no timeout on the self-test, so make sure --version cannot block.
Test the flow without replacing anything¶
$ mytool update check
new version available: 1.2.2 -> 1.2.3
$ mytool update run --dry-run --progress
update: checking…
update: downloading 4194304/8388608
update: verifying signature…
update: self-testing staged binary…
update: done — now at 1.2.3
dry run: staged 1.2.2 -> 1.2.3 at /home/you/.cache/mytool/update/v1.2.3/bin/mytool
--dry-run performs every step except the swap and tells you where the staged
binary is, so you can run it yourself. --progress prints each phase to stderr,
which is how you find out which step is slow or failing.
Nothing cleans the cache directory up. Each release leaves an archive and an extracted binary behind.
Decide about automatic checks — carefully¶
ToolMetadata::update_policy defaults to Disabled. Prompt prints a notice on
stderr when a newer release exists; Enabled performs the update before running the
user's command.
Under Enabled, any update failure fails the command the user actually asked for,
and there is no environment variable, config key or flag a user can set to opt out.
Prefer Prompt unless you have a specific reason not to. update_check_interval
throttles both, defaulting to 24 hours.
Related¶
updatereference — every flag, the ten steps, every failure code.- Automatic update checks
- What a self-update trusts, and why