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
↓ nina-af8ae2f6.tar.gzatlas at this save
files changed3 files
src/commands.zig+1 −1src/exec.zig+22 −9src/main.zig+28 −0src/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 ---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 {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" {