diff --git a/home/console/nix-flake.nix b/home/console/nix-flake.nix --- a/home/console/nix-flake.nix +++ b/home/console/nix-flake.nix @@ -20,8 +20,7 @@ config = lib.mkIf config.modules.terminal.flakeManagement.enable { # statix/deadnix are only ever invoked from this module, so they're - # pulled in here rather than via a separate linting module. alejandra - # is only pulled in directly when pedantix (which wraps it) is off. + # pulled in here rather than via a separate linting module. home.packages = with pkgs; [statix deadnix sops alejandra]; programs = { # Installs nh and sets NH_FLAKE so ad-hoc `nh os switch` etc. also @@ -39,32 +38,87 @@ then ''fd ".nix" $"${repo}/" | split row "\n" | each {pedantix $in} | ignore'' else ''alejandra $"${repo}"''; in '' + def progress-bar [step: int, total: int, --width: int = 12] { + let filled = (($step | into float) * $width / $total | math round | into int) + let bar = (1..$width | each { |i| if $i <= $filled { "█" } else { "░" } } | str join) + $"[($bar)] ($step)/($total)" + } + + # Unified flake management command. Flags select which stages run, but + # execution order is always pull -> update -> format/lint -> rebuild, + # regardless of flag order, since anything else risks flake.lock conflicts. + def flake [ + --pull (-p) # jj git pull, hard gate on unresolved conflicts + --update (-u) # nix flake update --flake ${repo} + --format (-f) # format + statix + deadnix (hard gates) + --rebuild (-r) # nh os switch, prints resulting generation + --help (-h) # show usage + ] { + if $help { + print "Usage: flake [-p|--pull] [-u|--update] [-f|--format] [-r|--rebuild]" + print "" + print " -p, --pull jj git pull (aborts on unresolved conflicts)" + print " -u, --update nix flake update" + print " -f, --format format .nix files, then statix + deadnix (hard gates)" + print " -r, --rebuild nh os switch" + print "" + print "Shortcuts:" + print ("nixfm = -f | rebuild = -pfr | upflake = -pfru" | split row "|" | split column "=" | str trim | rename Command Flags) + return + } + + let total = ([$pull $update $format $rebuild] | where { $in } | length) + if $total == 0 { + print "⚠️ No stage flags given — nothing to do. Try `flake --help`." + return + } + mut step = 0 + + if $pull { + $step += 1 + print $"⚠️ (progress-bar $step $total) Pulling latest changes..." + jj -R $"${repo}" git fetch + let conflicted = (jj -R $"${repo}" log -r "conflicts()" --no-graph -T "change_id ++ \"\n\"" | lines) + if ($conflicted | is-not-empty) { + error make { msg: $"jj pull produced conflicts, resolve before continuing:\n($conflicted | str join "\n")" } + } + print $"☑️ (progress-bar $step $total) Pulled cleanly." + } + + if $update { + $step += 1 + print $"⚠️ (progress-bar $step $total) Updating flake.lock..." + nix flake update --flake $"${repo}" + print $"☑️ (progress-bar $step $total) Updated flake.lock." + } + + if $format { + $step += 1 + print $"⚠️ (progress-bar $step $total) Formatting + linting..." + ${formatCmd} + statix check $"${repo}" + deadnix --fail $"${repo}" + print $"☑️ (progress-bar $step $total) Formatting + linting clean." + } + + if $rebuild { + $step += 1 + print $"⚠️ (progress-bar $step $total) Rebuilding & switching..." + nh os switch $"${repo}" -H ${hostName} --quiet --elevation-strategy sudo + nixos-rebuild list-generations | split row "\n" | split column " " | headers | get "Generation" | get 0 | $"✅ (progress-bar $step $total) Successfully built Generation ($in)" + } + } + # Format every .nix file, then lint. statix and deadnix are both # hard gates now: any non-zero exit from either aborts before a # broken/dead-code-laden commit can happen. - def nixfm [] { - ${formatCmd} - statix check $"${repo}" - deadnix --fail $"${repo}" - } + def nixfm [] { flake --format } # Format + lint, then rebuild and switch via nh. - def rebuild [] { - print "⚠️ [1/3] Initiated Rebuild!" - nixfm - print "☑️ [2/3] Finished Formatting! Starting Rebuild..." - nh os switch $"${repo}" -H ${hostName} --quiet --elevation-strategy sudo - nixos-rebuild list-generations | split row "\n" | split column " " | headers | get "Generation" | get 0 | $"✅ [3/3] Successfully build Generation ($in)" - } + def rebuild [] { flake --pull --format --rebuild } # Update the flake's inputs, then rebuild. - def upflake [] { - print "⚠️ [1/3] Initiated Flake Update!" - nix flake update --flake $"${repo}" - print "☑️ [2/3] Updated flake.lock! Triggering Rebuild..." - rebuild - print "✅ [3/3] Finished Flake Update!" - } + def upflake [] { flake --pull --update --format --rebuild } # Pull a live NetworkManager connection profile out of # /etc/NetworkManager/system-connections, sops-encrypt it as a