a178ca844c84

pkg commands: resolve as nixpkgs#name consistently — fixes why/deps/size/closure on flake systems (v3.1.5)

time
2026-06-10 21:27 UTC · 4 months ago
machine
koh2
parent
a512584d3c19
4 files VERSION+1 −1build.zig.zon+1 −1src/commands.zig+7 −9src/exec.zig+83 −15
VERSION+1 −1
@@ -1 +1 @@1-3.1.41+3.1.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.1.4",12+    .version = "3.1.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/commands.zig+7 −9
@@ -1435,19 +1435,17 @@14351435     if (ctx.sub.len == 0 or std.mem.eql(u8, ctx.sub, "help")) return output.subcommandHelp("pkg", &pkg_help, null);14361436     const name = if (ctx.args.len > 0) ctx.args[0] else return error.PackageNotFound;14371437     if (std.mem.eql(u8, ctx.sub, "why")) {1438-        _ = try exec.pkgWhy(ctx.io, &ctx.machine, ctx.gpa, name);1438+        if (try exec.pkgWhy(ctx.io, &ctx.machine, ctx.gpa, name) != 0) return error.PackageNotFound;14391439         return;14401440     }14411441     if (std.mem.eql(u8, ctx.sub, "deps")) {1442-        _ = try exec.pkgDeps(ctx.io, &ctx.machine, ctx.gpa, name);1442+        if (try exec.pkgDeps(ctx.io, &ctx.machine, ctx.gpa, name) != 0) return error.PackageNotFound;14431443         return;14441444     }14451445     if (std.mem.eql(u8, ctx.sub, "size")) {1446-        const s = exec.pkgSize(ctx.gpa, ctx.io, &ctx.machine, name) catch null;1447-        if (s) |sz| {1448-            defer ctx.gpa.free(sz);1449-            output.pkgResult("size", sz);1450-        }1446+        const s = try exec.pkgSize(ctx.gpa, ctx.io, &ctx.machine, name);1447+        defer ctx.gpa.free(s);1448+        output.pkgResult("size", s);14511449         return;14521450     }14531451     if (std.mem.eql(u8, ctx.sub, "path")) {@@ -1457,7 +1455,7 @@14571455         return;14581456     }14591457     if (std.mem.eql(u8, ctx.sub, "closure")) {1460-        _ = try exec.pkgClosure(ctx.io, &ctx.machine, ctx.gpa, name);1458+        if (try exec.pkgClosure(ctx.io, &ctx.machine, ctx.gpa, name) != 0) return error.PackageNotFound;14611459         return;14621460     }14631461 }@@ -1466,7 +1464,7 @@14661464 // without the user needing to know about the `pkg` subgroup.14671465 pub fn why(ctx: Ctx) !void {14681466     const name = if (ctx.args.len > 0) ctx.args[0] else return error.PackageNotFound;1469-    _ = try exec.pkgWhy(ctx.io, &ctx.machine, ctx.gpa, name);1467+    if (try exec.pkgWhy(ctx.io, &ctx.machine, ctx.gpa, name) != 0) return error.PackageNotFound;14701468 }14711469 14721470 // --- Pin / Unpin ---

view file

src/exec.zig+83 −15
@@ -960,35 +960,103 @@960960 961961 // --- Pkg info ---962962 963+fn pkgInstallable(gpa: std.mem.Allocator, pkg: []const u8) ![]u8 {964+    return std.fmt.allocPrint(gpa, "nixpkgs#{s}", .{pkg});965+}966+967+fn resolvePkgStorePath(gpa: std.mem.Allocator, io: std.Io, machine: *const types.Machine, installable: []const u8) ![]u8 {968+    const r = try capture(gpa, io, machine, &.{ "nix", "build", "--no-link", "--print-out-paths", installable });969+    defer gpa.free(r.stderr);970+    defer gpa.free(r.stdout);971+    if (r.exit_code != 0) return error.PackageNotFound;972+973+    var lines = std.mem.tokenizeAny(u8, r.stdout, "\r\n");974+    const first_path = lines.next() orelse return error.PackageNotFound;975+    const store_path = std.mem.trim(u8, first_path, " \t");976+    if (store_path.len == 0) return error.PackageNotFound;977+    return gpa.dupe(u8, store_path);978+}979+963980 pub fn pkgWhy(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8) !u8 {964-    return stream(io, machine, gpa, &.{ "nix", "why-depends", ".#", pkg });981+    const installable = try pkgInstallable(gpa, pkg);982+    defer gpa.free(installable);983+    const store_path = try resolvePkgStorePath(gpa, io, machine, installable);984+    defer gpa.free(store_path);985+    return stream(io, machine, gpa, &.{ "nix", "why-depends", "/run/current-system", store_path });965986 }966987 967988 pub fn pkgDeps(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8) !u8 {968-    return stream(io, machine, gpa, &.{ "nix-store", "--query", "--requisites", pkg });989+    const installable = try pkgInstallable(gpa, pkg);990+    defer gpa.free(installable);991+    const store_path = try resolvePkgStorePath(gpa, io, machine, installable);992+    defer gpa.free(store_path);993+    return stream(io, machine, gpa, &.{ "nix-store", "--query", "--requisites", store_path });969994 }970995 971996 pub fn pkgSize(gpa: std.mem.Allocator, io: std.Io, machine: *const types.Machine, pkg: []const u8) ![]u8 {972-    // A bare name ("ripgrep") isn't a valid installable; resolve via nixpkgs# like973-    // `pkg path` does. -S -h gives closure size, human-readable. Output is974-    // "<store-path>   <size>"; return just the size.975-    const ref = try std.fmt.allocPrint(gpa, "nixpkgs#{s}", .{pkg});976-    defer gpa.free(ref);977-    const r = try capture(gpa, io, machine, &.{ "nix", "path-info", "-S", "-h", ref });997+    const installable = try pkgInstallable(gpa, pkg);998+    defer gpa.free(installable);999+    const r = try capture(gpa, io, machine, &.{ "nix", "path-info", "-s", "--json", installable });9781000     defer gpa.free(r.stderr);9791001     defer gpa.free(r.stdout);9801002     if (r.exit_code != 0) return error.ProcessFailed;9811003 982-    const line = std.mem.trim(u8, r.stdout, " \t\n\r");983-    var it = std.mem.tokenizeAny(u8, line, " \t");984-    _ = it.next(); // store path985-    const size = std.mem.trim(u8, it.rest(), " \t");986-    if (size.len == 0) return error.ProcessFailed;987-    return gpa.dupe(u8, size);1004+    const nar_size = try pathInfoNarSize(gpa, r.stdout);1005+    return humanPkgSize(gpa, nar_size);9881006 }9891007 9901008 pub fn pkgClosure(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8) !u8 {991-    return stream(io, machine, gpa, &.{ "nix", "path-info", "-rS", pkg });1009+    const installable = try pkgInstallable(gpa, pkg);1010+    defer gpa.free(installable);1011+    const store_path = try resolvePkgStorePath(gpa, io, machine, installable);1012+    defer gpa.free(store_path);1013+    return stream(io, machine, gpa, &.{ "nix", "path-info", "-rS", store_path });1014+}1015+1016+fn pathInfoNarSize(gpa: std.mem.Allocator, json_str: []const u8) !u64 {1017+    var parsed = std.json.parseFromSlice(std.json.Value, gpa, json_str, .{}) catch return error.ProcessFailed;1018+    defer parsed.deinit();1019+    switch (parsed.value) {1020+        .object => |obj| {1021+            var it = obj.iterator();1022+            while (it.next()) |entry| {1023+                if (entry.value_ptr.* == .object) {1024+                    if (entry.value_ptr.object.get("narSize")) |v| {1025+                        if (v == .integer and v.integer >= 0) return @intCast(v.integer);1026+                    }1027+                }1028+            }1029+        },1030+        .array => |arr| {1031+            for (arr.items) |item| {1032+                if (item == .object) {1033+                    if (item.object.get("narSize")) |v| {1034+                        if (v == .integer and v.integer >= 0) return @intCast(v.integer);1035+                    }1036+                }1037+            }1038+        },1039+        else => {},1040+    }1041+    return error.ProcessFailed;1042+}1043+1044+fn humanPkgSize(gpa: std.mem.Allocator, bytes: u64) ![]u8 {1045+    const units = [_][]const u8{ "B", "KiB", "MiB", "GiB", "TiB" };1046+    var val: f64 = @floatFromInt(bytes);1047+    var unit: usize = 0;1048+    while (val >= 1024.0 and unit + 1 < units.len) : (unit += 1) val /= 1024.0;1049+    if (unit == 0) return std.fmt.allocPrint(gpa, "{d} {s}", .{ bytes, units[0] });1050+    return std.fmt.allocPrint(gpa, "{d:.1} {s}", .{ val, units[unit] });1051+}1052+1053+test "pathInfoNarSize parses object and array shapes" {1054+    try std.testing.expectEqual(@as(u64, 1064629862), try pathInfoNarSize(std.testing.allocator,1055+        \\{"/nix/store/abc-ffmpeg":{"narSize":1064629862}}1056+    ));1057+    try std.testing.expectEqual(@as(u64, 2048), try pathInfoNarSize(std.testing.allocator,1058+        \\[{"path":"/nix/store/abc-pkg","narSize":2048}]1059+    ));9921060 }9931061 9941062 // --- Hash / Fetch ---

view file