versioned installs and a real uninstall — rig's turn (box#79's layout, ported) #35

Closed
opened 2026-07-18 19:12:16 +00:00 by dan-claude-bot · 0 comments
dan-claude-bot commented 2026-07-18 19:12:16 +00:00 (Migrated from github.com)

box just landed versioned installs and a real uninstall (heavy-duty/box#79, merged); rig has the same disease in a worse form and should get the same cure.

Where rig is today

install.sh is the pre-versioning box shape, minus even the #66 guard:

rm -rf "$DEST"        # ← a stray re-run DELETES the working install, unconditionally
mv "$EXTRACTED" "$DEST"
ln -sf "$DEST/bin/rig" "$BINDIR/rig"
  • No VERSION file, so an install can't even say what it is (rig#32's first item).
  • No uninstall verb at all — removal is undocumented rm -rf prose.
  • "Upgrade" is re-run-and-clobber: no converge/no-op, no side-by-side, no way back. On a fleet where rig bootstrap is the machine's identity source, a broken main at the wrong moment bricks the tool with no use <previous> escape.

What box#79 proved out (adopt, not re-derive)

<root>/versions/<v>/       one full tree per version (its own VERSION + INSTALLED_FROM)
<root>/current -> versions/<v>
$BINDIR/rig -> current/bin/rig
  • rig versions / rig use <v> / rig uninstall [<v>|--all] as table verbs.
  • Same-version re-run → converging no-op (RIG_REINSTALL=1 replaces via two renames, delete last); new version → side-by-side.
  • Flat-tree migration in place (two renames, tree preserved bit for bit).
  • One strict valid_version gate everywhere a version becomes a path (box#79's review found the traversal hole; don't re-ship it).
  • Atomic current flips (link beside + mv -Tf), symlink healing judged from versions/<v> itself, never from a wedged link.
  • Uninstall ends with the absence assert: every removed path re-checked, survivors → exit 1 uninstall INCOMPLETE by name.
  • RIG_INSTALL_SOURCE=<dir-or-tarball> so the test suite drives real installer runs (box's tests went from greps to driven installs this way).

rig-specific deltas

  • The flip gate is different. box refuses the flip under existing boxes (#66 protects boxes). rig's equivalent stake is the converged host itself — a candidate gate: warn (not refuse) when /etc/rig/role exists, since switching versions under a bootstrapped host changes what a re-converge would do. Worth deciding deliberately rather than inheriting box's rule blind.
  • Simpler tiers. rig is root-oriented with $HOME/.local fallback; no multi-user grant surface to protect. current-chain resolution via readlink -f should port unchanged (bin/rig derives its root the same way).
  • Sequencing: wants rig#32's VERSION file first (the version key IS the tree's own VERSION); the tag-resolving installer and this layout are natural one-PR-each companions, same as box#79 → box#83.

Prior art to crib wholesale: box#79's install.sh, cmd_versions/cmd_use/cmd_uninstall, valid_version, flip_current, and its test/cli.sh install-drive machinery.

box just landed versioned installs and a real uninstall (heavy-duty/box#79, merged); rig has the same disease in a worse form and should get the same cure. ## Where rig is today `install.sh` is the pre-versioning box shape, minus even the #66 guard: ```bash rm -rf "$DEST" # ← a stray re-run DELETES the working install, unconditionally mv "$EXTRACTED" "$DEST" ln -sf "$DEST/bin/rig" "$BINDIR/rig" ``` - No `VERSION` file, so an install can't even say what it is (rig#32's first item). - No uninstall verb at all — removal is undocumented `rm -rf` prose. - "Upgrade" is re-run-and-clobber: no converge/no-op, no side-by-side, no way back. On a fleet where `rig bootstrap` is the machine's identity source, a broken main at the wrong moment bricks the tool with no `use <previous>` escape. ## What box#79 proved out (adopt, not re-derive) ``` <root>/versions/<v>/ one full tree per version (its own VERSION + INSTALLED_FROM) <root>/current -> versions/<v> $BINDIR/rig -> current/bin/rig ``` - `rig versions` / `rig use <v>` / `rig uninstall [<v>|--all]` as table verbs. - Same-version re-run → converging no-op (`RIG_REINSTALL=1` replaces via two renames, delete last); new version → side-by-side. - Flat-tree migration in place (two renames, tree preserved bit for bit). - One strict `valid_version` gate everywhere a version becomes a path (box#79's review found the traversal hole; don't re-ship it). - Atomic `current` flips (link beside + `mv -Tf`), symlink healing judged from `versions/<v>` itself, never from a wedged link. - Uninstall ends with the **absence assert**: every removed path re-checked, survivors → exit 1 `uninstall INCOMPLETE` by name. - `RIG_INSTALL_SOURCE=<dir-or-tarball>` so the test suite drives real installer runs (box's tests went from greps to driven installs this way). ## rig-specific deltas - **The flip gate is different.** box refuses the flip under existing *boxes* (#66 protects boxes). rig's equivalent stake is the converged host itself — a candidate gate: warn (not refuse) when `/etc/rig/role` exists, since switching versions under a bootstrapped host changes what a re-converge would do. Worth deciding deliberately rather than inheriting box's rule blind. - **Simpler tiers.** rig is root-oriented with `$HOME/.local` fallback; no multi-user grant surface to protect. `current`-chain resolution via `readlink -f` should port unchanged (`bin/rig` derives its root the same way). - **Sequencing:** wants rig#32's `VERSION` file first (the version key IS the tree's own VERSION); the tag-resolving installer and this layout are natural one-PR-each companions, same as box#79 → box#83. Prior art to crib wholesale: box#79's `install.sh`, `cmd_versions`/`cmd_use`/`cmd_uninstall`, `valid_version`, `flip_current`, and its `test/cli.sh` install-drive machinery.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/rig#35
No description provided.