4afdbd225c8f
add nina setup: detects and enables nix-command + flakes in ~/.config/nix/nix.conf
- time
- 2026-05-30 10:03 UTC · 4 months ago
- machine
- koh2
- agent
- claude-sonnet-4-6
- session
- nina setup command
- parent
- e6b790aa94d5
↓ nina-4afdbd22.tar.gzatlas at this save
files changed4 files
src/commands.zig+17 −0src/exec.zig+79 −0src/main.zig+13 −0src/output.zig+38 −0src/commands.zig+17 −0
@@ -681,6 +681,23 @@681681 output.hello(ctx.cfg.machines);682682 }683683 684+pub fn setup(gpa: std.mem.Allocator, io: std.Io, environ: *const std.process.Environ.Map) !void {685+ output.setupCheck();686+ const status = exec.checkNixFeatures(gpa, io, environ);687+ output.setupFeatures(status.nix_command, status.flakes);688+689+ if (status.allEnabled()) {690+ output.setupAllSet();691+ return;692+ }693+694+ output.setupEnabling();695+ const path = try exec.enableNixFeatures(gpa, io, environ);696+ defer gpa.free(path);697+698+ output.setupDone(path, exec.isNixOS(io));699+}700+684701 // --- Log helper ---685702 686703 fn logAction(ctx: Ctx, action: []const u8, gen: u32) void {src/exec.zig+79 −0
@@ -650,3 +650,82 @@650650 const size = file.length(io) catch 0;651651 file.writePositionalAll(io, entry, size) catch {};652652 }653+654+// --- Nix feature setup ---655+656+pub const NixFeatureStatus = struct {657+ nix_command: bool = false,658+ flakes: bool = false,659+660+ pub fn allEnabled(self: NixFeatureStatus) bool {661+ return self.nix_command and self.flakes;662+ }663+};664+665+fn scanConfFeatures(conf: []const u8, status: *NixFeatureStatus) void {666+ var lines = std.mem.splitScalar(u8, conf, '\n');667+ while (lines.next()) |line| {668+ const trimmed = std.mem.trim(u8, line, " \t");669+ if (!std.mem.startsWith(u8, trimmed, "experimental-features")) continue;670+ if (std.mem.indexOf(u8, trimmed, "nix-command") != null) status.nix_command = true;671+ if (std.mem.indexOf(u8, trimmed, "flakes") != null) status.flakes = true;672+ }673+}674+675+pub fn checkNixFeatures(gpa: std.mem.Allocator, io: std.Io, environ: *const std.process.Environ.Map) NixFeatureStatus {676+ var status = NixFeatureStatus{};677+678+ if (std.Io.Dir.readFileAlloc(.cwd(), io, "/etc/nix/nix.conf", gpa, .unlimited) catch null) |conf| {679+ defer gpa.free(conf);680+ scanConfFeatures(conf, &status);681+ }682+683+ if (environ.get("HOME")) |home| {684+ var buf: [512]u8 = undefined;685+ const path = std.fmt.bufPrint(&buf, "{s}/.config/nix/nix.conf", .{home}) catch return status;686+ if (std.Io.Dir.readFileAlloc(.cwd(), io, path, gpa, .unlimited) catch null) |conf| {687+ defer gpa.free(conf);688+ scanConfFeatures(conf, &status);689+ }690+ }691+692+ return status;693+}694+695+pub fn isNixOS(io: std.Io) bool {696+ const file = std.Io.Dir.openFile(.cwd(), io, "/etc/NIXOS", .{}) catch return false;697+ file.close(io);698+ return true;699+}700+701+// Appends `experimental-features = nix-command flakes` to ~/.config/nix/nix.conf.702+// Returns the path written (caller must free).703+pub fn enableNixFeatures(gpa: std.mem.Allocator, io: std.Io, environ: *const std.process.Environ.Map) ![]const u8 {704+ const home = environ.get("HOME") orelse {705+ errors.error_info.setMessage("HOME not set", .{});706+ return error.ConfigNotFound;707+ };708+709+ const dir_path = try std.fmt.allocPrint(gpa, "{s}/.config/nix", .{home});710+ defer gpa.free(dir_path);711+ const file_path = try std.fmt.allocPrint(gpa, "{s}/.config/nix/nix.conf", .{home});712+ errdefer gpa.free(file_path);713+714+ _ = captureLocal(gpa, io, &.{ "mkdir", "-p", dir_path }) catch {};715+716+ const line = "experimental-features = nix-command flakes\n";717+ const file = std.Io.Dir.openFile(.cwd(), io, file_path, .{ .mode = .read_write }) catch718+ (std.Io.Dir.createFile(.cwd(), io, file_path, .{}) catch {719+ errors.error_info.setMessage("could not write {s}", .{file_path});720+ return error.FileNotFound;721+ });722+ defer file.close(io);723+724+ const size = file.length(io) catch 0;725+ file.writePositionalAll(io, line, size) catch {726+ errors.error_info.setMessage("could not write {s}", .{file_path});727+ return error.FileNotFound;728+ };729+730+ return file_path;731+}src/main.zig+13 −0
@@ -42,6 +42,19 @@4242 return;4343 }4444 45+ // setup runs before config so it works as an onboarding step46+ if (std.mem.eql(u8, args.command, "setup")) {47+ commands.setup(gpa, io, startup.environ_map) catch |err| {48+ const msg = errors.error_info.getMessage() orelse errors.staticMessage(err);49+ const detail = errors.error_info.getDetail();50+ const suggestion = errors.error_info.getSuggestion();51+ output.printError(msg, detail, suggestion);52+ output.flush();53+ std.process.exit(1);54+ };55+ return;56+ }57+4558 // Load config and resolve machine4659 const cfg = config.load(arena, io, startup.environ_map) catch |err| {4760 const msg = errors.error_info.getMessage() orelse errors.staticMessage(err);src/output.zig+38 −0
@@ -608,6 +608,44 @@608608 p("{s}done\n", .{hdr()});609609 }610610 611+// --- Setup ---612+613+pub fn setupCheck() void {614+ p("{s}checking nix features\n", .{hdr()});615+}616+617+pub fn setupFeatures(nix_command: bool, flakes: bool) void {618+ p("{s}nix-command {s}{s}{s}\n", .{619+ step(),620+ if (nix_command) c(GREEN) else "",621+ if (nix_command) "enabled" else "not enabled",622+ if (nix_command) c(RESET) else "",623+ });624+ p("{s}flakes {s}{s}{s}\n", .{625+ step(),626+ if (flakes) c(GREEN) else "",627+ if (flakes) "enabled" else "not enabled",628+ if (flakes) c(RESET) else "",629+ });630+}631+632+pub fn setupAllSet() void {633+ p("{s}all set\n", .{hdr()});634+}635+636+pub fn setupEnabling() void {637+ p("{s}enabling experimental features\n", .{hdr()});638+}639+640+pub fn setupDone(path: []const u8, is_nixos: bool) void {641+ p("{s}wrote {s}\n", .{ step(), path });642+ p("{s}done open a new shell to pick up the change\n", .{hdr()});643+ if (is_nixos) {644+ printSubstep("to persist across nixos-rebuild, add to configuration.nix:", .{});645+ printSubstep(" nix.settings.experimental-features = [ \"nix-command\" \"flakes\" ];", .{});646+ }647+}648+611649 pub fn pinDone(input: []const u8, commit: []const u8) void {612650 p("{s}pinned {s} -> {s}\n", .{ hdr(), input, commit });613651 }