2e6bd97e4fc0
fix: progress bar tracks copies, error pipeline catches service activation failures (v3.2.5)
- time
- 2026-06-12 23:15 UTC · 4 months ago
- machine
- koh2
- agent
- claude-sonnet-4-6
- session
- progress bar and service failure fixes
- parent
- 63f60ea4c5c0
↓ nina-2e6bd97e.tar.gzatlas at this save
files changed4 files
VERSION+1 −1build.zig.zon+1 −1src/errors.zig+30 −0src/output.zig+5 −2build.zig.zon+1 −1
@@ -9,7 +9,7 @@99 .name = .nina,1010 // This is a [Semantic Version](https://semver.org/).1111 // In a future version of Zig it will be used for package deduplication.12- .version = "3.2.4",12+ .version = "3.2.5",1313 // Together with name, this represents a globally unique package1414 // identifier. This field is generated by the Zig toolchain when the1515 // package is first created, and then *never changes*. This allowssrc/errors.zig+30 −0
@@ -493,6 +493,26 @@493493 };494494 }495495 496+ // "warning: the following units failed: svc.service"497+ // switch-to-configuration exits 4 when one or more units fail to start.498+ if (std.mem.indexOf(u8, stderr, "the following units failed:") != null) {499+ const svc = blk: {500+ if (lineWith(stderr, "the following units failed:")) |l| {501+ if (std.mem.indexOf(u8, l, "failed:")) |p| {502+ const rest = std.mem.trim(u8, l[p + 7 ..], " \t");503+ if (rest.len > 0 and rest.len <= 80) break :blk rest;504+ }505+ }506+ break :blk "a service";507+ };508+ return .{509+ .title = "service failed after upgrade",510+ .body = std.fmt.allocPrint(allocator, "{s} failed to start after activation.", .{svc}) catch "a service failed to start after activation.",511+ .suggestion = std.fmt.allocPrint(allocator, "systemctl status {s}", .{svc}) catch "systemctl status <service>",512+ .is_system = true,513+ };514+ }515+496516 // ── home manager patterns ─────────────────────────────────────────────────497517 498518 if (std.mem.indexOf(u8, stderr, "The option") != null and@@ -652,6 +672,16 @@652672 test "layer 1: no match returns null" {653673 const t = matchKnownPattern(std.testing.allocator, "some unrecognized error text");654674 try std.testing.expect(t == null);675+}676+677+test "layer 1: service activation failure" {678+ const t = matchKnownPattern(std.testing.allocator,679+ "warning: the following units failed: rtorrent.service\n" ++680+ "Command 'systemd-run ...' returned non-zero exit status 4.");681+ try std.testing.expect(t != null);682+ try std.testing.expectEqualStrings("service failed after upgrade", t.?.title);683+ try std.testing.expect(std.mem.indexOf(u8, t.?.body, "rtorrent.service") != null);684+ try std.testing.expect(std.mem.indexOf(u8, t.?.suggestion, "rtorrent.service") != null);655685 }656686 657687 test "layer 3: fallback always returns" {src/output.zig+5 −2
@@ -87,8 +87,11 @@8787 return;8888 }8989 90- // "fetching path '/nix/store/...'"91- if (std.mem.indexOf(u8, line, "fetching path '") != null) {90+ // "copying path '/nix/store/...' from '...'" (nix 2.x substituter message)91+ // older nix used "fetching path '...'" — match both for compatibility92+ if (std.mem.indexOf(u8, line, "copying path '") != null or93+ std.mem.indexOf(u8, line, "fetching path '") != null)94+ {9295 self.completed +|= 1;9396 self.mode = .fetching;9497 self.setActiveName(line, false);