feat(release): ship the switch binary to Linux installs, as gvproxy-min - #994
Conversation
📝 WalkthroughWalkthroughThe change renames the installed gvproxy binary to Changesgvproxy-min rollout
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Installer
participant InstallRecord
participant Filesystem
participant Manifest
Installer->>Manifest: Read current component set
Installer->>InstallRecord: Read prior gvproxy entry
Installer->>Filesystem: Hash and inspect bin/gvproxy
Filesystem-->>Installer: File state and SHA-256
Installer->>Filesystem: Remove unchanged renamed file
Installer->>InstallRecord: Persist migrated record
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
The installer's `bin` prefix is `~/.local/bin`, which is on PATH, and podman/crc/Docker Desktop all ship their own `gvproxy` there. Under the upstream name whichever was installed last wins a PATH lookup — in either direction — so we take a minimal-specific name. The bytes are stock gvproxy, still pinned and SHA-256-verified by fetch-gvproxy.sh; only the installed name changes, and the release artifacts keep theirs. `switch::installed_gvproxy_bin()` becomes the one definition of where an install puts that binary, in the crate whose charter is already "one definition rather than drifting copies". This fixes a real bug on the way past: minimald only ever looked at the fixed system path, so a user-local install was invisible to it and own-IP could not find a switch it had. minvmd already had that tier and now delegates to the shared one. The system-wide fallback moves too, but that directory is not on PATH, so the collision rationale never applied there — and an operator who provisioned the old path has no install record for us to migrate. The resolver therefore still honours `/usr/lib/minimal/bin/gvproxy` when only that exists. install.sh reverses the rename from the prior record, on the same bytes-still-ours terms uninstall uses: the old file is removed only when its hash still matches what we recorded writing, so a user-replaced binary is kept and reported. Three rules keep that safe, all covered by the harness and specified as R6.1a: - skip entirely when the manifest THIS run installed still ships a `gvproxy` component. Channels advance independently, so a post-rename installer will be pointed at a pre-rename manifest, where the file on disk is the one we just placed — deleting it would leave the host with no switch binary at all, on every run. - report a refusal rather than deleting a modified file. - carry the row forward when removal fails, so the next run retries instead of stranding the binary the rename exists to remove. Split out of #988 to keep that PR to release packaging; it depends on none of it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9d5bb52 to
30fa133
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/specs/07-spec-installer/07-spec-installer.md`:
- Around line 353-356: In the “Retry a failure” text, replace “afterwards” with
the US-English spelling “afterward”; leave the rest of the migration behavior
description unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2581f4ab-bf86-4145-aeb1-ff513359e141
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
crates/minimal/src/diag/mod.rscrates/minimald/src/main.rscrates/minimald/src/server.rscrates/minvmd/src/image.rscrates/switch/Cargo.tomlcrates/switch/src/lib.rsdocs/reference/cli-minimald.mddocs/specs/07-spec-installer/07-spec-installer.mdscripts/install.shscripts/install_test.shscripts/stage-release.sh
| - **Retry a failure.** If removal fails (a read-only or root-owned `bin`), the | ||
| row is carried into this run's record so the next run tries again — without | ||
| it the migration gets exactly one attempt, because the record it reads is | ||
| replaced immediately afterwards. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Minor US-locale wording nit.
"afterwards" → "afterward" for American English consistency.
✏️ Proposed fix
- it, because the record it reads is replaced immediately afterwards.
+ it, because the record it reads is replaced immediately afterward.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - **Retry a failure.** If removal fails (a read-only or root-owned `bin`), the | |
| row is carried into this run's record so the next run tries again — without | |
| it the migration gets exactly one attempt, because the record it reads is | |
| replaced immediately afterwards. | |
| - **Retry a failure.** If removal fails (a read-only or root-owned `bin`), the | |
| row is carried into this run's record so the next run tries again — without | |
| it the migration gets exactly one attempt, because the record it reads is | |
| replaced immediately afterward. |
🧰 Tools
🪛 LanguageTool
[locale-violation] ~356-~356: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...cord it reads is replaced immediately afterwards. R6.2, If the resolved bin direc...
(AFTERWARDS_US)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/specs/07-spec-installer/07-spec-installer.md` around lines 353 - 356, In
the “Retry a failure” text, replace “afterwards” with the US-English spelling
“afterward”; leave the rest of the migration behavior description unchanged.
Source: Linters/SAST tools
gvproxy-mingvproxy-min
Split out of #988 to shrink its review surface. Independent of the rest of that PR.
Two halves of one story: the switch binary is unusable on installs — on macOS because of a
PATHcollision, on Linux because it is absent entirely.Linux: own-ip is dead on every installed host
The native (DM2) own-ip datapath is implemented (#542) and proven by
netns_root_integration.rs— butminimaldresolves a switch binary that no Linux install ships, so the spawn fails on a path that was never populated. CI only passes because it fetches gvproxy itself and setsGVPROXY_BIN.The release already builds pin-verified
gvproxy-linux-{amd64,arm64}; they were never mapped to install components. This adds the two rows. The installer is manifest-driven and needs no change.macOS: the name collides
The
binprefix is~/.local/bin, which is onPATH, and podman/crc/Docker Desktop all ship their owngvproxythere. Under the upstream name whichever was installed last wins a lookup — in either direction, so we can shadow theirs as easily as they shadow ours.The binary therefore installs as
gvproxy-min. The bytes are stock gvproxy, still pinned and SHA-256-verified byfetch-gvproxy.sh; only the installed name changes, and the release artifacts keepgvproxy-*.A second bug fixed on the way past
switch::installed_gvproxy_bin()becomes the single definition of where an install puts that binary — in the crate whose charter is already "one definition rather than drifting copies".Doing that surfaced an existing defect:
minimaldonly ever consulted the fixed system path, so even a user-local install was invisible to it.minvmdalready had that tier; it now delegates to the shared definition instead of keeping its own copy.Migration
The renamed component is a new manifest row, so the old
bin/gvproxystops appearing in future manifests and the uninstall record walk would never revisit it — it would sit onPATHforever, which is the collision this change exists to remove.install.shreverses the rename from the prior record, on the same bytes-still-ours terms uninstall uses (R7.3): removed only when its SHA-256 still equals what we recorded writing. Three rules keep that safe:gvproxy. Channels advance independently, so a post-rename installer will meet a pre-rename manifest. There the file on disk is the one this run just installed, and deleting it would leave the host with no switch binary at all, on every run. This is the sharp edge; its regression test is verified to fail without the guard.bin), the row is carried into this run's record so the next run retries — without it the migration gets one attempt, since the record it reads is replaced immediately after.Specified as R6.1a in spec 07, which had no requirement covering a record-driven migration during install.
The system-wide path
/usr/lib/minimal/bin/gvproxy→.../gvproxy-mintoo, for one name everywhere. But that directory is not onPATH, so the collision rationale never applied there, and an operator who provisioned it has no install record to migrate. The resolver still honours the pre-rename path when only that exists.What is deliberately not here
minvmd, libkrun, and the guest payload. That was already the plan for splitting #988; gominimal/pkgs#533 has since provenminvmdcan ship as a single static-musl binary (builds, boots a VM, passes the session e2e, verified against a stub control). Shipping the dynamic-glibc version now would put ~29 MB of libraries, RUNPATH rewriting, abin/libsibling constraint and a new glibc floor onto users' disks — then delete all of it days later.Verification
just cigreen;lint-shellclean; shellcheck clean under 0.10.0 as well as 0.11 (CI runs an older build that flagsSC2015where 0.11 no longer does).sh,dash, and macOSsh.switchunit tests cover both system-path branches deterministically via an internalresolve_installed(bin_dir, system, legacy), so they never probe the real/usr/lib/minimal/bin— otherwise they would flip on precisely the pre-rename hosts the legacy branch serves.stage-release.sh --dry-runshows all three rows landing atbin/gvproxy-min.🤖 Generated with Claude Code