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
4 files VERSION+1 −1build.zig.zon+1 −1src/errors.zig+30 −0src/output.zig+5 −2
VERSION+1 −1
@@ -1 +1 @@1-3.2.41+3.2.5

view file

build.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 allows

view file

src/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" {

view file

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);

view file