feat: add yt update self-replacing binary updater #24
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/self-update"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds
yt update. Downloads the platform-appropriate prebuilt binary from the Generic Package registry and atomically replaces the running executable.Configuring the URL
Per your question on this PR's parent message: the URL is the package directory base, not the repo URL. Specifically:
yt updateappends/latest/yt-<platform>to that base. Today:yt-linux-x86_64oryt-windows-x86_64.exe.Plumbing: a Forgejo Actions variable (
vars.YOUTRACK_CLI_UPDATE_URL) flows into the workflow env, then into a--build-argondocker buildx build, then intoARG+ENVinoci-build/Dockerfile{,.windows}, thenbuild.rsreads it and emitscargo:rustc-env=YOUTRACK_CLI_UPDATE_URL=<value>, thencommands/update.rsreads it viaenv!. If the Forgejo variable is unset,build.rsfalls back to the in-source default above so localcargobuilds and forks both work without ceremony.How the binary swap works
Atomic replacement uses the
self-replacecrate. Windows locks the running.exe, so a naivestd::fs::renameover the current binary breaks on that platform;self-replacehandles both unix and windows correctly. The flow: download to<current_exe>.new, chmod 0755 on unix, callself_replace::self_replace, clean up the staging file.Platform detection uses
cfg(target_os) + cfg(target_arch). Today we only ship Linux x86_64 and Windows x86_64; other targets get a clear "no prebuilt binary for target_os=X target_arch=Y" instead of a silent broken URL.Verification
cargo fmt --checkclean.cargo clippy --all-targets -- -D warningsclean.cargo test --all-targets: 181 tests pass (6 new).cargo run -- update --helpshows--urland--dry-run.cargo run -- update --dry-runprintsUpdating from https://dev.a8n.run/api/packages/pandoras-box/generic/youtrack-cli/latest/yt-linux-x86_64and exits 0 without making an HTTP call.Coverage on this PR: pure URL construction (with and without trailing slashes), baked-base-URL is non-empty (regression guard on build.rs), dry-run makes no HTTP call (wiremock-free test that would fail if a request leaked out), 404 response surfaces in the error. The self-replace path is intentionally not unit-tested because mucking with the test runner's own binary is bad form; real verification is
yt updateagainst the live registry.Setting the Forgejo variable
In
dev.a8n.run/pandoras-box/youtrack-cli/settings/actions/variables, add a repo variable namedYOUTRACK_CLI_UPDATE_URLset to:(Or leave it unset; the in-source default points at the same URL, so it only matters for forks pointing at a different registry.)
Test plan
YOUTRACK_CLI_UPDATE_URLbaked in.yt --versiondoesn't change;yt update --dry-runprints the URL.yt:yt update, thenyt --versionshows the build date moving to the newest CI run.yt update --url https://example.com/nothing/herereturns a clear 404 error.Sequence
This is part 2 of 3 for today's items. Part 1 (TODO.md + version metadata) is PR #23. Part 3 (per-instance config redesign) is next; it'll be a bigger PR since it reworks the on-disk layout that landed in PR #19.
yt updateself-replacing binary updaterNew top-level subcommand: `yt update [--url <override>] [--dry-run]`. Downloads the platform's prebuilt binary from the Forgejo Generic Package registry and atomically replaces the running executable. The package base URL is baked at build time from `YOUTRACK_CLI_UPDATE_URL`. The plumbing: a Forgejo Actions variable (`vars.YOUTRACK_CLI_UPDATE_URL`) flows into the workflow env, then into a `--build-arg` on `docker buildx build`, then into `ARG` + `ENV` in `oci-build/Dockerfile{,.windows}`, then `build.rs` reads it and emits `cargo:rustc-env=YOUTRACK_CLI_UPDATE_URL=<value>` for the compile, then `commands/update.rs` reads it via `env!`. If the Forgejo variable is unset, `build.rs` falls back to `https://dev.a8n.run/api/packages/pandoras-box/generic/youtrack-cli` so local cargo builds and forks both work without ceremony. The URL is the package directory base (NOT the repo URL): `yt update` appends `/latest/yt-<platform>` to it, mirroring the layout `build-linux.yml` and `build-windows.yml` publish. Platform detection uses `cfg(target_os)` + `cfg(target_arch)`. Today we only ship `yt-linux-x86_64` and `yt-windows-x86_64.exe`; other targets get an explicit "no prebuilt binary for target_os=X target_arch=Y" instead of a silent broken URL. Atomic replacement uses the `self-replace` crate (Armin Ronacher; the de facto answer for cross-platform self-update because Windows locks the running .exe). The flow: download to `<current_exe>.new`, chmod 0755 on unix, hand the path to `self_replace::self_replace`, mop up the staging file. Errors surface with the URL or path in context so failures are diagnosable from the message. `--dry-run` prints the URL it would hit and returns success without making any HTTP call or filesystem write; useful in CI smoke tests and for users who want to know what's about to happen before granting write access to their `$PATH`. `--url` overrides the baked URL for the run, mostly useful for tests but also lets forks point at their own registry without rebuilding. Tests: pure URL construction (with and without trailing slashes), baked-base-URL is non-empty (regression guard on build.rs), dry-run path makes no HTTP call, 404 response surfaces in the error. The self-replace path is intentionally not tested because mucking with the test runner's own binary is bad form; manual verification: `cargo run -- update --dry-run` prints the right URL, real `update` against the live registry replaces the binary in place. Adds `self-replace = "1"` to dependencies. 181 tests pass (6 new); clippy/fmt clean.