bbbdab1eedc3
setup: flake vs channel awareness in wizard and config — firstRunWizard asks channels(1)/flakes(2) after machine name, prompts for flake directory vs config path accordingly, and writes top-level 'flake = true' for flakes; NinaConfig.flake field + config parser + test; channel command group prints ':: note: using a flake system — channel commands may not apply' when cfg.flake (note: nina update is the self-updater here, so the note lives on the channel group) — build+tests green, both wizard paths and the note verified on temp HOME
- time
- 2026-06-10 01:08 UTC · 4 months ago
- machine
- koh2
- agent
- claude-opus-4-8
- session
- nina community improvements
- parent
- 74510ec9f7dd
↓ nina-bbbdab1e.tar.gzatlas at this save
files changed4 files
src/commands.zig+18 −8src/config.zig+23 −0src/output.zig+28 −0src/types.zig+2 −0src/commands.zig+18 −8
@@ -952,6 +952,8 @@952952 // --- Channel commands ---953953 954954 pub fn channel(ctx: Ctx) !void {955+ // On a flake-managed system, nix-channel operations generally don't apply.956+ if (ctx.cfg.flake) output.flakeChannelNote();955957 if (std.mem.eql(u8, ctx.sub, "list") or ctx.sub.len == 0) {956958 output.channelListHeader(ctx.machine.name);957959 const chans = try exec.channelList(ctx.gpa, ctx.io, &ctx.machine);@@ -1109,21 +1111,29 @@11091111 var local_buf: [256]u8 = undefined;11101112 11111113 const machine_name = output.firstRunPrompt("machine name", "my-machine", &name_buf);1112- const config_path = output.firstRunPrompt("config path", "/etc/nixos", &path_buf);1114+1115+ // Flake-based systems don't have a traditional /etc/nixos/configuration.nix —1116+ // their config lives wherever flake.nix is. Ask, then prompt for the right1117+ // path and record `flake = true` so channel commands can warn appropriately.1118+ const use_flakes = output.firstRunManageType();1119+ const config_path = if (use_flakes)1120+ output.firstRunPrompt("flake directory", "~/nixos-config", &path_buf)1121+ else1122+ output.firstRunPrompt("config path", "/etc/nixos", &path_buf);11131123 const local_str = output.firstRunPrompt("local", "true", &local_buf);11141124 11151125 const conf_path = try config.configPath(gpa, environ);11161126 defer gpa.free(conf_path);11171127 if (std.fs.path.dirname(conf_path)) |dir| exec.ensureDir(gpa, io, dir);11181128 1129+ // `flake` is a top-level config field, so it must precede the [machine] block.1130+ const flake_line = if (use_flakes) "flake = true\n" else "";11191131 var content_buf: [1024]u8 = undefined;1120- const content = try std.fmt.bufPrint(&content_buf,1121- \\[machine]1122- \\name = {s}1123- \\config = {s}1124- \\local = {s}1125- \\1126- , .{ machine_name, config_path, local_str });1132+ const content = try std.fmt.bufPrint(1133+ &content_buf,1134+ "{s}[machine]\nname = {s}\nconfig = {s}\nlocal = {s}\n",1135+ .{ flake_line, machine_name, config_path, local_str },1136+ );11271137 11281138 const file = std.Io.Dir.createFile(.cwd(), io, conf_path, .{}) catch {11291139 errors.error_info.setMessage("could not write {s}", .{conf_path});src/config.zig+23 −0
@@ -116,6 +116,8 @@116116 config.teach = parseBool(val, false);117117 } else if (std.mem.eql(u8, key, "color")) {118118 config.color = parseBool(val, true);119+ } else if (std.mem.eql(u8, key, "flake")) {120+ config.flake = parseBool(val, false);119121 }120122 }121123 @@ -158,6 +160,7 @@158160 std.debug.print("confirm={}\n", .{cfg.confirm});159161 std.debug.print("teach={}\n", .{cfg.teach});160162 std.debug.print("color={}\n", .{cfg.color});163+ std.debug.print("flake={}\n", .{cfg.flake});161164 for (cfg.machines, 0..) |m, i| {162165 std.debug.print("machine[{d}].name={s}\n", .{ i, m.name });163166 std.debug.print("machine[{d}].config={s}\n", .{ i, m.config_path });@@ -232,6 +235,26 @@232235 try std.testing.expect(config.color);233236 try std.testing.expect(!config.machines[0].local);234237 try std.testing.expect(config.machines[0].default);238+}239+240+test "parse flake flag" {241+ const with =242+ \\flake = true243+ \\244+ \\[machine]245+ \\name = box246+ ;247+ const cfg_with = try parse(std.testing.allocator, with, "/home/test");248+ defer std.testing.allocator.free(cfg_with.machines);249+ try std.testing.expect(cfg_with.flake);250+251+ const without =252+ \\[machine]253+ \\name = box254+ ;255+ const cfg_without = try parse(std.testing.allocator, without, "/home/test");256+ defer std.testing.allocator.free(cfg_without.machines);257+ try std.testing.expect(!cfg_without.flake);235258 }236259 237260 test "resolve machine by name" {src/output.zig+28 −0
@@ -985,6 +985,34 @@985985 return input;986986 }987987 988+// Ask whether the system is flake-based or channel-based. Returns true for989+// flakes. Defaults to channels (Enter or anything but '2'), echoing the choice.990+pub fn firstRunManageType() bool {991+ p("\n how do you manage your system?\n", .{});992+ p(" {s}[1]{s} nixos channels (classic)\n", .{ c(DIM), c(RESET) });993+ p(" {s}[2]{s} nix flakes\n\n", .{ c(DIM), c(RESET) });994+ p(" {s}> {s}", .{ c(DIM), c(RESET) });995+ flush();996+ var buf: [64]u8 = undefined;997+ var i: usize = 0;998+ while (i < buf.len - 1) {999+ const n = std.posix.read(std.posix.STDIN_FILENO, buf[i .. i + 1]) catch break;1000+ if (n == 0) break;1001+ if (buf[i] == '\n') break;1002+ i += 1;1003+ }1004+ const input = std.mem.trim(u8, buf[0..i], " \t\r");1005+ const flakes = input.len > 0 and input[0] == '2';1006+ p("{s}{s}{s}\n", .{ c(DIM), if (flakes) "nix flakes" else "nixos channels", c(RESET) });1007+ return flakes;1008+}1009+1010+// Shown before channel operations on a flake-managed system, where nix-channel1011+// commands generally don't apply.1012+pub fn flakeChannelNote() void {1013+ p("{s}{s}note: using a flake system — channel commands may not apply{s}\n", .{ hdr(), c(DIM), c(RESET) });1014+}1015+9881016 pub fn firstRunWritten(path: []const u8) void {9891017 p("\n {s}written to {s}{s}\n\n", .{ c(DIM), path, c(RESET) });9901018 }src/types.zig+2 −0
@@ -23,6 +23,8 @@2323 confirm: bool = true,2424 teach: bool = false,2525 color: bool = true,26+ // Flake-based system (vs nixos channels). Channel commands warn when set.27+ flake: bool = false,2628 machines: []Machine = &.{},2729 };2830