af8ae2f6db79

run: thread ctx.passthrough into nix run argv and stop nina's own flags matching after a literal -- separator

time
2026-07-02 00:57 UTC · 3 months ago
machine
koh2
agent
claude-sonnet-5
session
nina passthrough flag audit
parent
4c4db6e43144
3 files src/commands.zig+1 −1src/exec.zig+22 −9src/main.zig+28 −0
src/commands.zig+1 −1
@@ -1962,7 +1962,7 @@19621962 pub fn runCmd(ctx: Ctx) !void {19631963     const pkg_arg = if (ctx.args.len > 0) ctx.args[0] else return error.PackageNotFound;19641964     output.runStart(pkg_arg);1965-    _ = try exec.nixRun(ctx.io, &ctx.machine, ctx.gpa, pkg_arg);1965+    _ = try exec.nixRun(ctx.io, &ctx.machine, ctx.gpa, pkg_arg, ctx.passthrough);19661966 }19671967 19681968 // --- Hello ---

view file

src/exec.zig+22 −9
@@ -1091,19 +1091,32 @@10911091     return stream(io, machine, gpa, argv.items);10921092 }10931093 1094-pub fn nixRun(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8) !u8 {1094+pub fn nixRun(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8, extra_flags: []const []const u8) !u8 {10951095     // nix run always gets --no-write-lock-file for remote URLs10961096     const is_url = std.mem.startsWith(u8, pkg, "http://") or std.mem.startsWith(u8, pkg, "https://") or std.mem.startsWith(u8, pkg, "github:");1097+1098+    var argv: std.ArrayList([]const u8) = .empty;1099+    defer argv.deinit(gpa);1100+    try argv.append(gpa, "nix");1101+    try argv.append(gpa, "run");1102+1103+    var ref_buf: ?[]u8 = null;1104+    defer if (ref_buf) |r| gpa.free(r);10971105     if (is_url) {1098-        return stream(io, machine, gpa, &.{ "nix", "run", pkg, "--no-write-lock-file" });1106+        try argv.append(gpa, pkg);1107+        try argv.append(gpa, "--no-write-lock-file");1108+    } else if (std.mem.indexOfScalar(u8, pkg, '#') != null) {1109+        // Already a flake ref (contains '#') → use as-is; bare name → default to nixpkgs#.1110+        try argv.append(gpa, pkg);1111+    } else {1112+        ref_buf = try std.fmt.allocPrint(gpa, "nixpkgs#{s}", .{pkg});1113+        try argv.append(gpa, ref_buf.?);10991114     }1100-    // Already a flake ref (contains '#') → use as-is; bare name → default to nixpkgs#.1101-    if (std.mem.indexOfScalar(u8, pkg, '#') != null) {1102-        return stream(io, machine, gpa, &.{ "nix", "run", pkg });1103-    }1104-    const ref = try std.fmt.allocPrint(gpa, "nixpkgs#{s}", .{pkg});1105-    defer gpa.free(ref);1106-    return stream(io, machine, gpa, &.{ "nix", "run", ref });1115+1116+    // extra_flags carries any `-- <program args>` separator verbatim, so this1117+    // naturally reconstructs `nix run <ref> -- <args>` — nix's own convention.1118+    try argv.appendSlice(gpa, extra_flags);1119+    return stream(io, machine, gpa, argv.items);11071120 }11081121 11091122 pub fn nixShell(io: std.Io, machine: *const types.Machine, gpa: std.mem.Allocator, pkg: []const u8) !u8 {

view file

src/main.zig+28 −0
@@ -278,8 +278,22 @@278278         }279279     }280280 281+    // Once a literal `--` separator is seen, everything after it (and the282+    // separator itself) is opaque data for the wrapped program — e.g. `nina run283+    // hello -- --version` must forward `--version` to hello, not have nina steal284+    // it as its own --version flag. Stop matching nina's own flags at that point.285+    var past_separator = false;281286     while (i < argv.len) : (i += 1) {282287         const a = argv[i];288+        if (past_separator) {289+            try passthrough.append(arena, a);290+            continue;291+        }292+        if (std.mem.eql(u8, a, "--")) {293+            past_separator = true;294+            try passthrough.append(arena, a);295+            continue;296+        }283297         if (std.mem.eql(u8, a, "--on") or std.mem.eql(u8, a, "-on")) {284298             i += 1;285299             if (i < argv.len) result.machine = argv[i];@@ -415,6 +429,20 @@415429     try std.testing.expectEqualStrings("develop", args.command);416430     try std.testing.expectEqual(@as(usize, 1), args.passthrough.len);417431     try std.testing.expectEqualStrings("--show-trace", args.passthrough[0]);432+}433+434+test "parse args forwards run's -- separator and program args" {435+    const gpa = std.testing.allocator;436+    const argv = [_][:0]const u8{ "nina", "run", "hello", "--", "--version" };437+    const args = try parseArgs(gpa, &argv);438+    defer gpa.free(args.rest);439+    defer gpa.free(args.passthrough);440+    try std.testing.expectEqualStrings("run", args.command);441+    try std.testing.expectEqual(@as(usize, 1), args.rest.len);442+    try std.testing.expectEqualStrings("hello", args.rest[0]);443+    try std.testing.expectEqual(@as(usize, 2), args.passthrough.len);444+    try std.testing.expectEqualStrings("--", args.passthrough[0]);445+    try std.testing.expectEqualStrings("--version", args.passthrough[1]);418446 }419447 420448 test "parse subcommand" {

view file