d5b8315c6ade
self-update builds the new version into the store before touching the profile, so a network drop or eval failure never leaves nina uninstalled (NINA-006)
- time
- 2026-07-02 13:36 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- fable audit fix — commands.zig
- parent
- 74c9cc1a35ab
↓ nina-d5b8315c.tar.gzatlas at this save
files changed2 files
src/commands.zig+19 −0src/output.zig+13 −3src/commands.zig+19 −0
@@ -423,6 +423,25 @@423423 const element = nina_element.?;424424 output.updateSelfNixManaged(remote_ver);425425 if (ctx.cfg.confirm and !output.confirm("")) return;426+ // Build the new version into the store BEFORE touching the profile. A427+ // network drop or eval failure here leaves the old element untouched —428+ // this is the only step that can fail for reasons outside our control,429+ // so it must happen first. Once this succeeds, the subsequent430+ // remove+add operates on an already-warm local store and is not431+ // expected to fail for the same reasons, closing the window a raw432+ // remove-then-add would leave open between the two profile edits.433+ const prefetch_r = exec.capture(ctx.gpa, ctx.io, &ctx.machine, &.{ "nix", "build", "--no-link", "--refresh", FLAKE_URL }) catch {434+ output.updateSelfNixPrefetchFailed();435+ return;436+ };437+ defer ctx.gpa.free(prefetch_r.stdout);438+ defer ctx.gpa.free(prefetch_r.stderr);439+ if (prefetch_r.exit_code != 0) {440+ output.raw(prefetch_r.stdout);441+ output.raw(prefetch_r.stderr);442+ output.updateSelfNixPrefetchFailed();443+ return;444+ }426445 // `nix profile upgrade` re-resolves the element's own originalUrl. The427446 // archive install runs with --no-write-lock-file, so nix has nowhere to428447 // persist the lock it takes at install time except baking it into thatsrc/output.zig+13 −3
@@ -1129,10 +1129,20 @@11291129 p("{s}try manually: nix profile upgrade '.*nina.*'\n", .{step()});11301130 }11311131 1132+// Reached when building the new version into the store fails, before the1133+// profile has been touched at all — the old nina element is still installed1134+// and working.1135+pub fn updateSelfNixPrefetchFailed() void {1136+ p("{s}could not build the new version — check your connection and try again\n", .{if (color_enabled) ERR_C else ERR_P});1137+ p("{s}your current nina install is untouched\n", .{step()});1138+}1139+11321140 // Reached only after `nix profile remove` already succeeded and the1133-// re-add failed — nina is genuinely uninstalled at this point, not just1134-// stuck on the old version, so the recovery command must reinstall from1135-// scratch rather than upgrade.1141+// re-add failed despite the new version having just been built into the1142+// store — nina is genuinely uninstalled at this point, not just stuck on1143+// the old version, so the recovery command must reinstall from scratch1144+// rather than upgrade. Expected to be rare now that the build happens1145+// before the profile is touched.11361146 pub fn updateSelfNixReinstallFailed() void {11371147 p("{s}nix profile add failed — nina was removed and is not reinstalled\n", .{if (color_enabled) ERR_C else ERR_P});11381148 p("{s}reinstall manually: nix profile add --no-write-lock-file 'https://kepr.uk/nina/archive/HEAD.tar.gz#nina'\n", .{step()});