From a6ede7f49701cc406e702ec98e1802c5320f8872 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 10:43:06 -0700 Subject: [PATCH 01/10] nix-output-monitor: allow pinning a specific Nix version I'm interested in using `nom` for `nixos-rebuild`, which wants to do this. In order to avoid the patch breaking on upgrade without someone noticing, it's applied unilaterally, just with do-nothing replacements. --- .../by-name/ni/nix-output-monitor/package.nix | 18 ++++++ .../pin-a-specific-nix.patch | 62 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 pkgs/by-name/ni/nix-output-monitor/pin-a-specific-nix.patch diff --git a/pkgs/by-name/ni/nix-output-monitor/package.nix b/pkgs/by-name/ni/nix-output-monitor/package.nix index c50be7ede2a14..7eec76506f379 100644 --- a/pkgs/by-name/ni/nix-output-monitor/package.nix +++ b/pkgs/by-name/ni/nix-output-monitor/package.nix @@ -3,10 +3,24 @@ haskellPackages, installShellFiles, lib, + nix, + replaceVars, + + # Allow pinning a specific Nix version. + withPinnedNix ? false, }: let inherit (haskell.lib.compose) justStaticExecutables overrideCabal; + # If we're pinning Nix, then substitute with a Nix path; otherwise, just the name of the binary. + ambientOrPinned = name: if withPinnedNix then lib.getExe' nix name else name; + + pinnedNixPatch = replaceVars ./pin-a-specific-nix.patch { + nix = ambientOrPinned "nix"; + nix-build = ambientOrPinned "nix-build"; + nix-shell = ambientOrPinned "nix-shell"; + }; + overrides = { passthru.updateScript = ./update.sh; @@ -15,6 +29,9 @@ let testTargets = [ "unit-tests" ]; buildTools = [ installShellFiles ]; + + patches = [ pinnedNixPatch ]; + postInstall = '' ln -s nom "$out/bin/nom-build" ln -s nom "$out/bin/nom-shell" @@ -22,6 +39,7 @@ let installShellCompletion completions/* ''; }; + raw-pkg = haskellPackages.callPackage ./generated-package.nix { }; in lib.pipe raw-pkg [ diff --git a/pkgs/by-name/ni/nix-output-monitor/pin-a-specific-nix.patch b/pkgs/by-name/ni/nix-output-monitor/pin-a-specific-nix.patch new file mode 100644 index 0000000000000..99be3611ce663 --- /dev/null +++ b/pkgs/by-name/ni/nix-output-monitor/pin-a-specific-nix.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Philip Taron +Date: Thu, 23 Oct 2025 10:32:59 -0700 +Subject: [PATCH] exe: allow pinning a specific Nix version + +--- + exe/Main.hs | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/exe/Main.hs b/exe/Main.hs +index 5e0f010..01b9e06 100644 +--- a/exe/Main.hs ++++ b/exe/Main.hs +@@ -72,18 +72,18 @@ runApp :: String -> [String] -> IO Void + runApp = \cases + _ ["--version"] -> do + hPutStrLn stderr ("nix-output-monitor " <> fromString (showVersion version)) +- exitWith =<< runProcess (proc "nix" ["--version"]) +- "nom-build" args -> exitWith =<< runMonitoredCommand defaultConfig (proc "nix-build" (withJSON args)) ++ exitWith =<< runProcess (proc "@nix@" ["--version"]) ++ "nom-build" args -> exitWith =<< runMonitoredCommand defaultConfig (proc "@nix-build@" (withJSON args)) + "nom-shell" args -> do +- exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "nix-shell" (withJSON args <> ["--run", "exit"])) +- exitWith =<< runProcess (proc "nix-shell" args) +- "nom" ("build" : args) -> exitWith =<< runMonitoredCommand defaultConfig (proc "nix" ("build" : withJSON args)) ++ exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "@nix-shell@" (withJSON args <> ["--run", "exit"])) ++ exitWith =<< runProcess (proc "@nix-shell@" args) ++ "nom" ("build" : args) -> exitWith =<< runMonitoredCommand defaultConfig (proc "@nix@" ("build" : withJSON args)) + "nom" ("shell" : args) -> do +- exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "nix" ("shell" : withJSON (replaceCommandWithExit args))) +- exitWith =<< runProcess (proc "nix" ("shell" : args)) ++ exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "@nix@" ("shell" : withJSON (replaceCommandWithExit args))) ++ exitWith =<< runProcess (proc "@nix@" ("shell" : args)) + "nom" ("develop" : args) -> do +- exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "nix" ("develop" : withJSON (replaceCommandWithExit args))) +- exitWith =<< runProcess (proc "nix" ("develop" : args)) ++ exitOnFailure =<< runMonitoredCommand defaultConfig{silent = True} (proc "@nix@" ("develop" : withJSON (replaceCommandWithExit args))) ++ exitWith =<< runProcess (proc "@nix@" ("develop" : args)) + "nom" [] -> do + finalState <- monitorHandle @OldStyleInput defaultConfig{piping = True} stdin + if CMap.size finalState.fullSummary.failedBuilds + length finalState.nixErrors == 0 +@@ -108,7 +108,7 @@ printNixCompletion = \cases + exitSuccess + "nom" args@(sub_cmd : _) + | sub_cmd `elem` knownSubCommands -> +- exitWith =<< Process.runProcess (Process.proc "nix" args) ++ exitWith =<< Process.runProcess (Process.proc "@nix@" args) + prog args -> do + putTextLn $ "No completion support for " <> unwords (toText <$> prog : args) + exitFailure +@@ -170,7 +170,7 @@ monitorHandle config input_handle = withParser @a \streamParser -> do + Terminal.hHideCursor outputHandle + hSetBuffering stdout (BlockBuffering (Just 1_000_000)) + +- current_system <- Exception.handle ((Nothing <$) . printIOException) $ Just . decodeUtf8 <$> Process.readProcessStdout_ (Process.proc "nix" ["eval", "--extra-experimental-features", "nix-command", "--impure", "--raw", "--expr", "builtins.currentSystem"]) ++ current_system <- Exception.handle ((Nothing <$) . printIOException) $ Just . decodeUtf8 <$> Process.readProcessStdout_ (Process.proc "@nix@" ["eval", "--extra-experimental-features", "nix-command", "--impure", "--raw", "--expr", "builtins.currentSystem"]) + first_state <- initalStateFromBuildPlatform current_system + -- We enforce here, that the state type is completely strict so that we don‘t accumulate thunks while running the program. + let first_process_state = MkProcessState (StrictType.Strict $ firstState @a first_state) (stateToText config first_state) +-- +2.51.0 + From 7154574955e547c2b25ac97d9ac3a3c9bf687b86 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Wed, 22 Oct 2025 16:25:32 -0700 Subject: [PATCH 02/10] nixos-rebuild-ng: use callPackage from python3Packages This enables removal of the `with` statements in the derivation. --- pkgs/by-name/ni/nixos-rebuild-ng/package.nix | 166 +++-------------- pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 171 ++++++++++++++++++ .../ni/nixos-rebuild-ng/tests/repl.nix | 2 +- 3 files changed, 193 insertions(+), 146 deletions(-) create mode 100644 pkgs/by-name/ni/nixos-rebuild-ng/python.nix diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix index 33f52e0e1c68c..8add24cc58cb0 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix @@ -1,162 +1,38 @@ { - lib, stdenv, - callPackage, + python3Packages, + + # These are required to be passed through. installShellFiles, + lixPackageSets, mkShell, nix, - python3, - python3Packages, - runCommand, + nixVersions, + nixos-rebuild-ng, + nixosTests, scdoc, + withNgSuffix ? true, withReexec ? false, withShellFiles ? true, # Very long tmp dirs lead to "too long for Unix domain socket" # SSH ControlPath errors. Especially macOS sets long TMPDIR paths. withTmpdir ? if stdenv.hostPlatform.isDarwin then "/tmp" else null, - # passthru.tests - nixosTests, - nixVersions, - lixPackageSets, - nixos-rebuild-ng, }: -let - executable = if withNgSuffix then "nixos-rebuild-ng" else "nixos-rebuild"; -in -python3Packages.buildPythonApplication rec { - pname = "nixos-rebuild-ng"; - version = lib.trivial.release; - src = ./src; - pyproject = true; - - build-system = with python3Packages; [ - setuptools - ]; - nativeBuildInputs = lib.optionals withShellFiles [ +python3Packages.callPackage ./python.nix { + inherit installShellFiles - python3Packages.shtab + lixPackageSets + mkShell + nix + nixVersions + nixos-rebuild-ng + nixosTests scdoc - ]; - - propagatedBuildInputs = [ - # Make sure that we use the Nix package we depend on, not something - # else from the PATH for nix-{env,instantiate,build}. This is - # important, because NixOS defaults the architecture of the rebuilt - # system to the architecture of the nix-* binaries used. So if on an - # amd64 system the user has an i686 Nix package in her PATH, then we - # would silently downgrade the whole system to be i686 NixOS on the - # next reboot. - # The binary will be included in the wrapper for Python. - (lib.getBin nix) - ]; - - postPatch = '' - substituteInPlace nixos_rebuild/constants.py \ - --subst-var-by executable ${executable} \ - --subst-var-by withReexec ${lib.boolToString withReexec} \ - --subst-var-by withShellFiles ${lib.boolToString withShellFiles} - - substituteInPlace pyproject.toml \ - --replace-fail nixos-rebuild ${executable} - ''; - - postInstall = lib.optionalString withShellFiles '' - scdoc < ${./nixos-rebuild.8.scd} > ${executable}.8 - installManPage ${executable}.8 - - installShellCompletion --cmd ${executable} \ - --bash <(shtab --shell bash nixos_rebuild.get_main_parser) \ - --zsh <(shtab --shell zsh nixos_rebuild.get_main_parser) - ''; - - nativeCheckInputs = with python3Packages; [ - pytestCheckHook - ]; - - pytestFlags = [ "-vv" ]; - - makeWrapperArgs = lib.optionals (withTmpdir != null) [ - "--set TMPDIR ${withTmpdir}" - ]; - - passthru = - let - python-with-pkgs = python3.withPackages ( - ps: with ps; [ - mypy - pytest - # this is to help development (e.g.: better diffs) inside devShell - # only, do not use its helpers like `mocker` - pytest-mock - ruff - ] - ); - in - { - devShell = mkShell { - packages = [ python-with-pkgs ]; - shellHook = '' - cd pkgs/by-name/ni/nixos-rebuild-ng/src || true - ''; - }; - - tests = { - with_reexec = nixos-rebuild-ng.override { - withReexec = true; - withNgSuffix = false; - }; - with_nix_latest = nixos-rebuild-ng.override { - nix = nixVersions.latest; - }; - with_nix_stable = nixos-rebuild-ng.override { - nix = nixVersions.stable; - }; - with_nix_2_28 = nixos-rebuild-ng.override { - # oldest supported version in nixpkgs - nix = nixVersions.nix_2_28; - }; - with_lix_latest = nixos-rebuild-ng.override { - nix = lixPackageSets.latest.lix; - }; - with_lix_stable = nixos-rebuild-ng.override { - nix = lixPackageSets.stable.lix; - }; - - inherit (nixosTests) - # FIXME: this test is disabled since it times out in @ofborg - # nixos-rebuild-install-bootloader-ng - nixos-rebuild-specialisations-ng - nixos-rebuild-target-host-ng - ; - repl = callPackage ./tests/repl.nix { }; - # NOTE: this is a passthru test rather than a build-time test because we - # want to keep the build closures small - linters = runCommand "${pname}-linters" { nativeBuildInputs = [ python-with-pkgs ]; } '' - export MYPY_CACHE_DIR="$(mktemp -d)" - export RUFF_CACHE_DIR="$(mktemp -d)" - - pushd ${src} - echo -e "\x1b[32m## run mypy\x1b[0m" - mypy . - echo -e "\x1b[32m## run ruff\x1b[0m" - ruff check . - echo -e "\x1b[32m## run ruff format\x1b[0m" - ruff format --check . - popd - - touch $out - ''; - }; - }; - - meta = { - description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote"; - homepage = "https://github.com/NixOS/nixpkgs/tree/master/pkgs/by-name/ni/nixos-rebuild-ng"; - license = lib.licenses.mit; - maintainers = [ ]; - teams = [ lib.teams.nixos-rebuild ]; - mainProgram = executable; - }; + withNgSuffix + withReexec + withShellFiles + withTmpdir + ; } diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix new file mode 100644 index 0000000000000..4317f810778f4 --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -0,0 +1,171 @@ +{ + lib, + + buildPythonApplication, + pkgs, + pytestCheckHook, + python, + setuptools, + shtab, + + # Passed through from `./package.nix`. + installShellFiles, + lixPackageSets, + mkShell, + nix, + nixVersions, + nixos-rebuild-ng, + nixosTests, + runCommand, + scdoc, + + # Override interface, required to be passed in from `./package.nix`. + withNgSuffix, + withReexec, + withShellFiles, + withTmpdir, +}: +let + executable = if withNgSuffix then "nixos-rebuild-ng" else "nixos-rebuild"; +in +buildPythonApplication rec { + pname = "nixos-rebuild-ng"; + version = lib.trivial.release; + + src = ./src; + + pyproject = true; + + build-system = [ setuptools ]; + + nativeBuildInputs = lib.optionals withShellFiles [ + installShellFiles + shtab + scdoc + ]; + + propagatedBuildInputs = [ + # Make sure that we use the Nix package we depend on, not something + # else from the PATH for nix-{env,instantiate,build}. This is + # important, because NixOS defaults the architecture of the rebuilt + # system to the architecture of the nix-* binaries used. So if on an + # amd64 system the user has an i686 Nix package in her PATH, then we + # would silently downgrade the whole system to be i686 NixOS on the + # next reboot. + # The binary will be included in the wrapper for Python. + (lib.getBin nix) + ]; + + postPatch = '' + substituteInPlace nixos_rebuild/constants.py \ + --subst-var-by executable ${executable} \ + --subst-var-by withReexec ${lib.boolToString withReexec} \ + --subst-var-by withShellFiles ${lib.boolToString withShellFiles} + + substituteInPlace pyproject.toml \ + --replace-fail nixos-rebuild ${executable} + ''; + + postInstall = lib.optionalString withShellFiles '' + scdoc < ${./nixos-rebuild.8.scd} > ${executable}.8 + installManPage ${executable}.8 + + installShellCompletion --cmd ${executable} \ + --bash <(shtab --shell bash nixos_rebuild.get_main_parser) \ + --zsh <(shtab --shell zsh nixos_rebuild.get_main_parser) + ''; + + nativeCheckInputs = [ pytestCheckHook ]; + + pytestFlags = [ "-vv" ]; + + makeWrapperArgs = lib.optionals (withTmpdir != null) [ + "--set TMPDIR ${withTmpdir}" + ]; + + passthru = + let + python-with-pkgs = python.withPackages ( + ps: with ps; [ + mypy + pytest + # this is to help development (e.g.: better diffs) inside devShell + # only, do not use its helpers like `mocker` + pytest-mock + ruff + ] + ); + in + { + devShell = mkShell { + packages = [ python-with-pkgs ]; + shellHook = '' + cd pkgs/by-name/ni/nixos-rebuild-ng/src || true + ''; + }; + + tests = { + with_reexec = nixos-rebuild-ng.override { + withReexec = true; + withNgSuffix = false; + }; + + with_nix_latest = nixos-rebuild-ng.override { + nix = nixVersions.latest; + }; + + with_nix_stable = nixos-rebuild-ng.override { + nix = nixVersions.stable; + }; + + with_nix_2_28 = nixos-rebuild-ng.override { + # oldest supported version in nixpkgs + nix = nixVersions.nix_2_28; + }; + + with_lix_latest = nixos-rebuild-ng.override { + nix = lixPackageSets.latest.lix; + }; + + with_lix_stable = nixos-rebuild-ng.override { + nix = lixPackageSets.stable.lix; + }; + + inherit (nixosTests) + # FIXME: this test is disabled since it times out in @ofborg + # nixos-rebuild-install-bootloader-ng + nixos-rebuild-specialisations-ng + nixos-rebuild-target-host-ng + ; + + repl = pkgs.callPackage ./tests/repl.nix { }; + + # NOTE: this is a passthru test rather than a build-time test because we + # want to keep the build closures small + linters = runCommand "${pname}-linters" { nativeBuildInputs = [ python-with-pkgs ]; } '' + export MYPY_CACHE_DIR="$(mktemp -d)" + export RUFF_CACHE_DIR="$(mktemp -d)" + + pushd ${src} + echo -e "\x1b[32m## run mypy\x1b[0m" + mypy . + echo -e "\x1b[32m## run ruff\x1b[0m" + ruff check . + echo -e "\x1b[32m## run ruff format\x1b[0m" + ruff format --check . + popd + + touch $out + ''; + }; + }; + + meta = { + description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote"; + homepage = "https://github.com/NixOS/nixpkgs/tree/master/pkgs/by-name/ni/nixos-rebuild-ng"; + license = lib.licenses.mit; + maintainers = [ ]; + teams = [ lib.teams.nixos-rebuild ]; + mainProgram = executable; + }; +} diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/tests/repl.nix b/pkgs/by-name/ni/nixos-rebuild-ng/tests/repl.nix index 1a614e8d86553..a5a1322860e29 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/tests/repl.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/tests/repl.nix @@ -1,11 +1,11 @@ { lib, + stdenv, expect, nix, nixos-rebuild-ng, path, runCommand, - stdenv, writeText, }: let From 2f3f59b5d92f2d11bae4b990aba690404a9a8439 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Wed, 22 Oct 2025 16:30:32 -0700 Subject: [PATCH 03/10] nixos-rebuild-ng.tests.linters: fix build --- pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py | 4 ++-- pkgs/by-name/ni/nixos-rebuild-ng/src/tests/helpers.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py index 6cdfaba456c6e..90c12261025dc 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/utils.py @@ -90,11 +90,11 @@ def tabulate( def format_row(row: Mapping[str, Any]) -> str: s = (2 * " ").join( f"{str(row[header]).ljust(width)}" - for header, width in zip(data_headers, column_widths) + for header, width in zip(data_headers, column_widths, strict=True) ) return s.strip() - result = [format_row(dict(zip(data_headers, data_headers)))] + result = [format_row(dict(zip(data_headers, data_headers, strict=True)))] for row in data: result.append(format_row(row)) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/helpers.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/helpers.py index c7a2a29ee3dcd..f27e491a632d8 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/helpers.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/helpers.py @@ -1,5 +1,6 @@ +from collections.abc import Callable from types import ModuleType -from typing import Any, Callable +from typing import Any def get_qualified_name( From effe7014bccaffb2a344048d722264e184675e19 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Wed, 22 Oct 2025 16:36:15 -0700 Subject: [PATCH 04/10] nixos-rebuild-ng: extract linters passthru to separate file --- pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 18 ++--------- .../ni/nixos-rebuild-ng/tests/linters.nix | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 pkgs/by-name/ni/nixos-rebuild-ng/tests/linters.nix diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index 4317f810778f4..ae03551174a28 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -28,7 +28,7 @@ let executable = if withNgSuffix then "nixos-rebuild-ng" else "nixos-rebuild"; in -buildPythonApplication rec { +buildPythonApplication { pname = "nixos-rebuild-ng"; version = lib.trivial.release; @@ -142,21 +142,7 @@ buildPythonApplication rec { # NOTE: this is a passthru test rather than a build-time test because we # want to keep the build closures small - linters = runCommand "${pname}-linters" { nativeBuildInputs = [ python-with-pkgs ]; } '' - export MYPY_CACHE_DIR="$(mktemp -d)" - export RUFF_CACHE_DIR="$(mktemp -d)" - - pushd ${src} - echo -e "\x1b[32m## run mypy\x1b[0m" - mypy . - echo -e "\x1b[32m## run ruff\x1b[0m" - ruff check . - echo -e "\x1b[32m## run ruff format\x1b[0m" - ruff format --check . - popd - - touch $out - ''; + linters = pkgs.callPackage ./tests/linters.nix { }; }; }; diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/tests/linters.nix b/pkgs/by-name/ni/nixos-rebuild-ng/tests/linters.nix new file mode 100644 index 0000000000000..f52e5c321f0bc --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild-ng/tests/linters.nix @@ -0,0 +1,30 @@ +{ + runCommand, + python3, + nixos-rebuild-ng, +}: + +runCommand "lint-nixos-rebuild-ng" + { + nativeBuildInputs = [ + (python3.withPackages (ps: [ + ps.mypy + ps.ruff + ])) + ]; + } + '' + export MYPY_CACHE_DIR="$(mktemp -d)" + export RUFF_CACHE_DIR="$(mktemp -d)" + + pushd ${nixos-rebuild-ng.src} + echo -e "\x1b[32m## run mypy\x1b[0m" + mypy . + echo -e "\x1b[32m## run ruff\x1b[0m" + ruff check . + echo -e "\x1b[32m## run ruff format\x1b[0m" + ruff format --check . + popd + + touch $out + '' From 25492df8eaba4436e3865df20af697a00e7a9e92 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Wed, 22 Oct 2025 16:44:46 -0700 Subject: [PATCH 05/10] nixos-rebuild-ng: replace postPatch with a real patch It's a little strange to patch something that's in Nixpkgs, but it works great. --- .../nixos-rebuild-ng/0001-replacements.patch | 47 +++++++++++++++++++ pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 21 ++++----- .../src/nixos_rebuild/constants.py | 14 ++---- 3 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch new file mode 100644 index 0000000000000..deceabacd2534 --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Philip Taron +Date: Wed, 22 Oct 2025 17:02:15 -0700 +Subject: [PATCH] nixos-rebuild-ng: patch out actual replacements + +Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src` +--- + nixos_rebuild/constants.py | 6 +++--- + pyproject.toml | 6 +++--- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/nixos_rebuild/constants.py b/nixos_rebuild/constants.py +index c567796c590b..daa856a11430 100644 +--- a/nixos_rebuild/constants.py ++++ b/nixos_rebuild/constants.py +@@ -2,6 +2,6 @@ from typing import Final + + # These are replaced in a patch for the actual derivation; what's below supports running the tool + # out of the Nixpkgs repository directly. +-EXECUTABLE: Final[str] = "nixos-rebuild-ng" +-WITH_REEXEC: Final[bool] = True +-WITH_SHELL_FILES: Final[bool] = True ++EXECUTABLE: Final[str] = "@executable@" ++WITH_REEXEC: Final[bool] = @withReexec@ ++WITH_SHELL_FILES: Final[bool] = @withShellFiles@ +diff --git a/pyproject.toml b/pyproject.toml +index 757067db9f06..68dfa825a824 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -3,11 +3,11 @@ requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] +-name = "nixos-rebuild" +-version = "0.0.0" ++name = "@executable@" ++version = "@version@" + + [project.scripts] +-nixos-rebuild = "nixos_rebuild:main" ++@executable@ = "nixos_rebuild:main" + + [tool.setuptools.package-data] + nixos_rebuild = ["*.nix.template"] +-- +2.51.0 + diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index ae03551174a28..aff7e03be351a 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -16,7 +16,7 @@ nixVersions, nixos-rebuild-ng, nixosTests, - runCommand, + replaceVars, scdoc, # Override interface, required to be passed in from `./package.nix`. @@ -27,10 +27,11 @@ }: let executable = if withNgSuffix then "nixos-rebuild-ng" else "nixos-rebuild"; + version = lib.trivial.release; in buildPythonApplication { pname = "nixos-rebuild-ng"; - version = lib.trivial.release; + inherit version; src = ./src; @@ -56,15 +57,13 @@ buildPythonApplication { (lib.getBin nix) ]; - postPatch = '' - substituteInPlace nixos_rebuild/constants.py \ - --subst-var-by executable ${executable} \ - --subst-var-by withReexec ${lib.boolToString withReexec} \ - --subst-var-by withShellFiles ${lib.boolToString withShellFiles} - - substituteInPlace pyproject.toml \ - --replace-fail nixos-rebuild ${executable} - ''; + patches = [ + (replaceVars ./0001-replacements.patch { + inherit executable version; + withReexec = if withReexec then "True" else "False"; + withShellFiles = if withShellFiles then "True" else "False"; + }) + ]; postInstall = lib.optionalString withShellFiles '' scdoc < ${./nixos-rebuild.8.scd} > ${executable}.8 diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py index 03d4bb79eb618..c567796c590b5 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py @@ -1,11 +1,7 @@ -# mypy: disable-error-code=comparison-overlap from typing import Final -# Build-time flags -# Use strings to avoid breaking standalone (e.g.: `python -m nixos_rebuild`) -# usage -EXECUTABLE: Final[str] = "@executable@" -# Use either `== "true"` if the default (e.g.: `python -m nixos_rebuild`) is -# `False` or `!= "false"` if the default is `True` -WITH_REEXEC: Final[bool] = "@withReexec@" == "true" -WITH_SHELL_FILES: Final[bool] = "@withShellFiles@" == "true" +# These are replaced in a patch for the actual derivation; what's below supports running the tool +# out of the Nixpkgs repository directly. +EXECUTABLE: Final[str] = "nixos-rebuild-ng" +WITH_REEXEC: Final[bool] = True +WITH_SHELL_FILES: Final[bool] = True From c39ea916a6f137b0f162dabb3d61e4268187946c Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 05:18:51 -0700 Subject: [PATCH 06/10] nixos-rebuild-ng: extract constants for every Nix command This provides the place to customize the Nix being executed without needing to use `propagatedBuildInputs`. --- .../nixos-rebuild-ng/0001-replacements.patch | 26 ++++- pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 26 ++--- .../src/nixos_rebuild/constants.py | 9 ++ .../nixos-rebuild-ng/src/nixos_rebuild/nix.py | 49 +++++---- .../nixos-rebuild-ng/src/tests/test_main.py | 102 ++++++++++-------- .../ni/nixos-rebuild-ng/src/tests/test_nix.py | 73 +++++++------ 6 files changed, 168 insertions(+), 117 deletions(-) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch index deceabacd2534..b832cc322d1f5 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch @@ -5,15 +5,15 @@ Subject: [PATCH] nixos-rebuild-ng: patch out actual replacements Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src` --- - nixos_rebuild/constants.py | 6 +++--- - pyproject.toml | 6 +++--- - 2 files changed, 6 insertions(+), 6 deletions(-) + nixos_rebuild/constants.py | 20 ++++++++++---------- + pyproject.toml | 6 +++--- + 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/nixos_rebuild/constants.py b/nixos_rebuild/constants.py -index c567796c590b..daa856a11430 100644 +index 16164bbcd60b..e4423988c98a 100644 --- a/nixos_rebuild/constants.py +++ b/nixos_rebuild/constants.py -@@ -2,6 +2,6 @@ from typing import Final +@@ -2,15 +2,15 @@ from typing import Final # These are replaced in a patch for the actual derivation; what's below supports running the tool # out of the Nixpkgs repository directly. @@ -23,6 +23,22 @@ index c567796c590b..daa856a11430 100644 +EXECUTABLE: Final[str] = "@executable@" +WITH_REEXEC: Final[bool] = @withReexec@ +WITH_SHELL_FILES: Final[bool] = @withShellFiles@ + + # These names are replaced with absolute paths to Nix in the store. +-NIX: Final[str] = "nix" +-NIX_BUILD: Final[str] = "nix-build" +-NIX_CHANNEL: Final[str] = "nix-channel" +-NIX_COPY_CLOSURE: Final[str] = "nix-copy-closure" +-NIX_ENV: Final[str] = "nix-env" +-NIX_INSTANTIATE: Final[str] = "nix-instantiate" +-NIX_STORE: Final[str] = "nix-store" ++NIX: Final[str] = "@nix@" ++NIX_BUILD: Final[str] = "@nix-build@" ++NIX_CHANNEL: Final[str] = "@nix-channel@" ++NIX_COPY_CLOSURE: Final[str] = "@nix-copy-closure@" ++NIX_ENV: Final[str] = "@nix-env@" ++NIX_INSTANTIATE: Final[str] = "@nix-instantiate@" ++NIX_STORE: Final[str] = "@nix-store@" diff --git a/pyproject.toml b/pyproject.toml index 757067db9f06..68dfa825a824 100644 --- a/pyproject.toml diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index aff7e03be351a..95f570b78465f 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -45,23 +45,25 @@ buildPythonApplication { scdoc ]; - propagatedBuildInputs = [ - # Make sure that we use the Nix package we depend on, not something - # else from the PATH for nix-{env,instantiate,build}. This is - # important, because NixOS defaults the architecture of the rebuilt - # system to the architecture of the nix-* binaries used. So if on an - # amd64 system the user has an i686 Nix package in her PATH, then we - # would silently downgrade the whole system to be i686 NixOS on the - # next reboot. - # The binary will be included in the wrapper for Python. - (lib.getBin nix) - ]; - patches = [ (replaceVars ./0001-replacements.patch { inherit executable version; withReexec = if withReexec then "True" else "False"; withShellFiles = if withShellFiles then "True" else "False"; + + # Make sure that we use the Nix package we depend on, not the ambient Nix in the PATH. + # + # This is important, because NixOS defaults the architecture of the rebuilt system to the + # architecture of the nix-* binaries used. So if on an amd64 system the user has an i686 Nix + # package in her PATH, then we would silently downgrade the whole system to be i686 NixOS on + # the next reboot. + nix = lib.getExe' nix "nix"; + nix-build = lib.getExe' nix "nix-build"; + nix-channel = lib.getExe' nix "nix-channel"; + nix-copy-closure = lib.getExe' nix "nix-copy-closure"; + nix-env = lib.getExe' nix "nix-env"; + nix-instantiate = lib.getExe' nix "nix-instantiate"; + nix-store = lib.getExe' nix "nix-store"; }) ]; diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py index c567796c590b5..16164bbcd60bb 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py @@ -5,3 +5,12 @@ EXECUTABLE: Final[str] = "nixos-rebuild-ng" WITH_REEXEC: Final[bool] = True WITH_SHELL_FILES: Final[bool] = True + +# These names are replaced with absolute paths to Nix in the store. +NIX: Final[str] = "nix" +NIX_BUILD: Final[str] = "nix-build" +NIX_CHANNEL: Final[str] = "nix-channel" +NIX_COPY_CLOSURE: Final[str] = "nix-copy-closure" +NIX_ENV: Final[str] = "nix-env" +NIX_INSTANTIATE: Final[str] = "nix-instantiate" +NIX_STORE: Final[str] = "nix-store" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py index 9a7062c816ebb..1821dc263877a 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py @@ -13,6 +13,15 @@ from typing import Final, Literal from . import tmpdir +from .constants import ( + NIX, + NIX_BUILD, + NIX_CHANNEL, + NIX_COPY_CLOSURE, + NIX_ENV, + NIX_INSTANTIATE, + NIX_STORE, +) from .models import ( Action, BuildAttr, @@ -57,7 +66,7 @@ def build( Returns the built attribute as path. """ run_args = [ - "nix-build", + NIX_BUILD, build_attr.path, "--attr", build_attr.to_attr(attr), @@ -77,7 +86,7 @@ def build_flake( Returns the built attribute as path. """ run_args = [ - "nix", + NIX, *FLAKE_FLAGS, "build", "--print-out-paths", @@ -101,7 +110,7 @@ def build_remote( # > by the garbage collector r = run_wrapper( [ - "nix-instantiate", + NIX_INSTANTIATE, build_attr.path, "--attr", build_attr.to_attr(attr), @@ -122,7 +131,7 @@ def build_remote( try: r = run_wrapper( [ - "nix-store", + NIX_STORE, "--realise", drv, "--add-root", @@ -152,7 +161,7 @@ def build_remote_flake( ) -> Path: r = run_wrapper( [ - "nix", + NIX, *FLAKE_FLAGS, "eval", "--raw", @@ -165,7 +174,7 @@ def build_remote_flake( copy_closure(drv, to_host=build_host, from_host=None, copy_flags=copy_flags) r = run_wrapper( [ - "nix", + NIX, *FLAKE_FLAGS, "build", f"{drv}^*", @@ -196,7 +205,7 @@ def copy_closure( def nix_copy_closure(host: Remote, to: bool) -> None: run_wrapper( [ - "nix-copy-closure", + NIX_COPY_CLOSURE, *dict_to_flags(copy_flags), "--to" if to else "--from", host.host, @@ -208,7 +217,7 @@ def nix_copy_closure(host: Remote, to: bool) -> None: def nix_copy(to_host: Remote, from_host: Remote) -> None: run_wrapper( [ - "nix", + NIX, *FLAKE_FLAGS, "copy", *dict_to_flags(copy_flags), @@ -248,7 +257,7 @@ def edit_flake(flake: Flake | None, flake_flags: Args | None = None) -> None: "Try to find and open NixOS configuration file in editor for Flake config." run_wrapper( [ - "nix", + NIX, *FLAKE_FLAGS, "edit", *dict_to_flags(flake_flags), @@ -262,7 +271,7 @@ def edit_flake(flake: Flake | None, flake_flags: Args | None = None) -> None: def find_file(file: str, nix_flags: Args | None = None) -> Path | None: "Find classic Nix file location." r = run_wrapper( - ["nix-instantiate", "--find-file", file, *dict_to_flags(nix_flags)], + [NIX_INSTANTIATE, "--find-file", file, *dict_to_flags(nix_flags)], stdout=PIPE, check=False, ) @@ -283,7 +292,7 @@ def get_build_image_name( ) r = run_wrapper( [ - "nix-instantiate", + NIX_INSTANTIATE, "--eval", "--strict", "--json", @@ -310,7 +319,7 @@ def get_build_image_name_flake( ) -> str: r = run_wrapper( [ - "nix", + NIX, "eval", "--json", flake.to_attr( @@ -335,7 +344,7 @@ def get_build_image_variants( ) r = run_wrapper( [ - "nix-instantiate", + NIX_INSTANTIATE, "--eval", "--strict", "--json", @@ -361,7 +370,7 @@ def get_build_image_variants_flake( ) -> ImageVariants: r = run_wrapper( [ - "nix", + NIX, "eval", "--json", flake.to_attr("config.system.build.images"), @@ -450,7 +459,7 @@ def get_generations_from_nix_env( # Using `nix-env --list-generations` needs root to lock the profile r = run_wrapper( - ["nix-env", "-p", profile.path, "--list-generations"], + [NIX_ENV, "-p", profile.path, "--list-generations"], stdout=PIPE, remote=target_host, sudo=sudo, @@ -534,7 +543,7 @@ def get_generation_info(generation: Generation) -> GenerationJson: def repl(build_attr: BuildAttr, nix_flags: Args | None = None) -> None: - run_args = ["nix", "repl", "--file", build_attr.path] + run_args = [NIX, "repl", "--file", build_attr.path] if build_attr.attr: run_args.append(build_attr.attr) run_wrapper([*run_args, *dict_to_flags(nix_flags)]) @@ -554,7 +563,7 @@ def repl_flake(flake: Flake, flake_flags: Args | None = None) -> None: ) run_wrapper( [ - "nix", + NIX, *FLAKE_FLAGS, "repl", "--impure", @@ -568,7 +577,7 @@ def repl_flake(flake: Flake, flake_flags: Args | None = None) -> None: def rollback(profile: Profile, target_host: Remote | None, sudo: bool) -> Path: "Rollback Nix profile, like one created by `nixos-rebuild switch`." run_wrapper( - ["nix-env", "--rollback", "-p", profile.path], + [NIX_ENV, "--rollback", "-p", profile.path], remote=target_host, sudo=sudo, ) @@ -631,7 +640,7 @@ def set_profile( raise NixOSRebuildError(msg) run_wrapper( - ["nix-env", "-p", profile.path, "--set", path_to_config], + [NIX_ENV, "-p", profile.path, "--set", path_to_config], remote=target_host, sudo=sudo, ) @@ -700,7 +709,7 @@ def upgrade_channels(all_channels: bool = False, sudo: bool = False) -> None: or (channel_path / ".update-on-nixos-rebuild").exists() ): run_wrapper( - ["nix-channel", "--update", channel_path.name], + [NIX_CHANNEL, "--update", channel_path.name], check=False, sudo=sudo, ) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py index cdd4e65b17e6f..fe2da287ba880 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py @@ -10,6 +10,14 @@ import pytest import nixos_rebuild as nr +from nixos_rebuild.constants import ( + NIX, + NIX_BUILD, + NIX_COPY_CLOSURE, + NIX_ENV, + NIX_INSTANTIATE, + NIX_STORE, +) from .helpers import get_qualified_name @@ -139,11 +147,11 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix-instantiate": + if args[0] == NIX_INSTANTIATE: return CompletedProcess([], 0, str(nixpkgs_path)) elif args[0] == "git" and "rev-parse" in args: return CompletedProcess([], 0, "nixpkgs-rev") - elif args[0] == "nix-build": + elif args[0] == NIX_BUILD: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -156,7 +164,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: mock_run.assert_has_calls( [ call( - ["nix-instantiate", "--find-file", "nixpkgs", "-vvv"], + [NIX_INSTANTIATE, "--find-file", "nixpkgs", "-vvv"], stdout=PIPE, check=False, **DEFAULT_RUN_KWARGS, @@ -174,7 +182,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-build", + NIX_BUILD, "", "--attr", "config.system.build.toplevel", @@ -187,7 +195,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-env", + NIX_ENV, "-p", Path("/nix/var/nix/profiles/system"), "--set", @@ -229,7 +237,7 @@ def test_execute_nix_build_vm(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix-build": + if args[0] == NIX_BUILD: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -254,7 +262,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix-build", + NIX_BUILD, "", "--attr", "config.system.build.vm", @@ -278,13 +286,13 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix" and "eval" in args: + if args[0] == NIX and "eval" in args: return CompletedProcess( [], 0, '"nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd"', ) - elif args[0] == "nix": + elif args[0] == NIX: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -307,7 +315,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix", + NIX, "eval", "--json", '/path/to/config#nixosConfigurations."hostname".config.system.build.images', @@ -320,7 +328,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -333,7 +341,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix", + NIX, "eval", "--json", '/path/to/config#nixosConfigurations."hostname".config.system.build.images.azure.passthru.filePath', @@ -357,7 +365,7 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix": + if args[0] == NIX: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -386,7 +394,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -405,7 +413,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: call( [ "sudo", - "nix-env", + NIX_ENV, "-p", Path("/nix/var/nix/profiles/system"), "--set", @@ -459,13 +467,13 @@ def test_execute_nix_switch_build_target_host( config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix": + if args[0] == NIX: return CompletedProcess([], 0, str(config_path)) - elif args[0] == "nix-instantiate" and "--find-file" in args: + elif args[0] == NIX_INSTANTIATE and "--find-file" in args: return CompletedProcess([], 1) - elif args[0] == "nix-instantiate": + elif args[0] == NIX_INSTANTIATE: return CompletedProcess([], 0, str(config_path)) - elif args[0] == "ssh" and "nix-store" in args: + elif args[0] == "ssh" and NIX_STORE in args: return CompletedProcess([], 0, "/tmp/tmpdir/config") elif args[0] == "ssh" and "mktemp" in args: return CompletedProcess([], 0, "/tmp/tmpdir") @@ -501,7 +509,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix-instantiate", + NIX_INSTANTIATE, "--find-file", "nixpkgs", "--include", @@ -515,7 +523,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-instantiate", + NIX_INSTANTIATE, "", "--attr", "config.system.build.toplevel", @@ -531,7 +539,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: **DEFAULT_RUN_KWARGS, ), call( - ["nix-copy-closure", "--to", "user@build-host", config_path], + [NIX_COPY_CLOSURE, "--to", "user@build-host", config_path], check=True, **DEFAULT_RUN_KWARGS, ), @@ -556,7 +564,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: *nr.process.SSH_DEFAULT_OPTS, "user@build-host", "--", - "nix-store", + NIX_STORE, "--realise", str(config_path), "--add-root", @@ -595,7 +603,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "copy", @@ -615,7 +623,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: "user@target-host", "--", "sudo", - "nix-env", + NIX_ENV, "-p", "/nix/var/nix/profiles/system", "--set", @@ -673,7 +681,7 @@ def test_execute_nix_switch_flake_target_host( config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix": + if args[0] == NIX: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -698,7 +706,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -711,7 +719,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: **DEFAULT_RUN_KWARGS, ), call( - ["nix-copy-closure", "--to", "user@localhost", config_path], + [NIX_COPY_CLOSURE, "--to", "user@localhost", config_path], check=True, **DEFAULT_RUN_KWARGS, ), @@ -722,7 +730,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: "user@localhost", "--", "sudo", - "nix-env", + NIX_ENV, "-p", "/nix/var/nix/profiles/system", "--set", @@ -780,9 +788,9 @@ def test_execute_nix_switch_flake_build_host( config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix" and "eval" in args: + if args[0] == NIX and "eval" in args: return CompletedProcess([], 0, str(config_path)) - elif args[0] == "ssh" and "nix" in args: + elif args[0] == "ssh" and NIX in args: return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -806,7 +814,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "eval", @@ -818,7 +826,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: **DEFAULT_RUN_KWARGS, ), call( - ["nix-copy-closure", "--to", "user@localhost", config_path], + [NIX_COPY_CLOSURE, "--to", "user@localhost", config_path], check=True, **DEFAULT_RUN_KWARGS, ), @@ -828,7 +836,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: *nr.process.SSH_DEFAULT_OPTS, "user@localhost", "--", - "nix", + NIX, "--extra-experimental-features", "'nix-command flakes'", "build", @@ -842,7 +850,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-copy-closure", + NIX_COPY_CLOSURE, "--from", "user@localhost", config_path, @@ -852,7 +860,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-env", + NIX_ENV, "-p", Path("/nix/var/nix/profiles/system"), "--set", @@ -885,7 +893,7 @@ def test_execute_switch_rollback(mock_run: Mock, tmp_path: Path) -> None: nixpkgs_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix-instantiate": + if args[0] == NIX_INSTANTIATE: return CompletedProcess([], 0, str(nixpkgs_path)) elif args[0] == "git": return CompletedProcess([], 0, "") @@ -911,7 +919,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: mock_run.assert_has_calls( [ call( - ["nix-instantiate", "--find-file", "nixpkgs"], + [NIX_INSTANTIATE, "--find-file", "nixpkgs"], check=False, stdout=PIPE, **DEFAULT_RUN_KWARGS, @@ -931,7 +939,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - "nix-env", + NIX_ENV, "--rollback", "-p", Path("/nix/var/nix/profiles/system"), @@ -972,7 +980,7 @@ def test_execute_build(mock_run: Mock, tmp_path: Path) -> None: [ call( [ - "nix-build", + NIX_BUILD, "", "--attr", "config.system.build.toplevel", @@ -1015,7 +1023,7 @@ def test_execute_build_dry_run_build_and_target_remote( [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "eval", @@ -1027,7 +1035,7 @@ def test_execute_build_dry_run_build_and_target_remote( **DEFAULT_RUN_KWARGS, ), call( - ["nix-copy-closure", "--to", "user@build-host", config_path], + [NIX_COPY_CLOSURE, "--to", "user@build-host", config_path], check=True, **DEFAULT_RUN_KWARGS, ), @@ -1037,7 +1045,7 @@ def test_execute_build_dry_run_build_and_target_remote( *nr.process.SSH_DEFAULT_OPTS, "user@build-host", "--", - "nix", + NIX, "--extra-experimental-features", "'nix-command flakes'", "build", @@ -1059,7 +1067,7 @@ def test_execute_test_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix": + if args[0] == NIX: return CompletedProcess([], 0, str(config_path)) elif args[0] == "test": return CompletedProcess([], 1) @@ -1077,7 +1085,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -1112,7 +1120,7 @@ def test_execute_test_rollback( mock_run: Mock, ) -> None: def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == "nix-env": + if args[0] == NIX_ENV: return CompletedProcess( [], 0, @@ -1138,7 +1146,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - "nix-env", + NIX_ENV, "-p", Path("/nix/var/nix/profiles/system-profiles/foo"), "--list-generations", diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py index 6cc525410fe48..4f12e5bb1eb7f 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py @@ -11,6 +11,15 @@ import nixos_rebuild.models as m import nixos_rebuild.nix as n import nixos_rebuild.process as p +from nixos_rebuild.constants import ( + NIX, + NIX_BUILD, + NIX_CHANNEL, + NIX_COPY_CLOSURE, + NIX_ENV, + NIX_INSTANTIATE, + NIX_STORE, +) from .helpers import get_qualified_name @@ -28,7 +37,7 @@ def test_build(mock_run: Mock) -> None: ) == Path("/path/to/file") mock_run.assert_called_with( [ - "nix-build", + NIX_BUILD, "", "--attr", "config.system.build.attr", @@ -42,7 +51,7 @@ def test_build(mock_run: Mock) -> None: "config.system.build.attr", m.BuildAttr(Path("file"), "preAttr") ) == Path("/path/to/file") mock_run.assert_called_with( - ["nix-build", Path("file"), "--attr", "preAttr.config.system.build.attr"], + [NIX_BUILD, Path("file"), "--attr", "preAttr.config.system.build.attr"], stdout=PIPE, ) @@ -63,7 +72,7 @@ def test_build_flake(mock_run: Mock, monkeypatch: MonkeyPatch, tmpdir: Path) -> ) == Path("/path/to/file") mock_run.assert_called_with( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -88,11 +97,11 @@ def test_build_remote( def run_wrapper_side_effect( args: list[str], **kwargs: Any ) -> CompletedProcess[str]: - if args[0] == "nix-instantiate": + if args[0] == NIX_INSTANTIATE: return CompletedProcess([], 0, stdout=" \n/path/to/file\n ") elif args[0] == "mktemp": return CompletedProcess([], 0, stdout=" \n/tmp/tmpdir\n ") - elif args[0] == "nix-store": + elif args[0] == NIX_STORE: return CompletedProcess([], 0, stdout=" \n/tmp/tmpdir/config\n ") elif args[0] == "readlink": return CompletedProcess([], 0, stdout=" \n/path/to/config\n ") @@ -115,7 +124,7 @@ def run_wrapper_side_effect( [ call( [ - "nix-instantiate", + NIX_INSTANTIATE, "", "--attr", "preAttr.config.system.build.toplevel", @@ -127,7 +136,7 @@ def run_wrapper_side_effect( ), call( [ - "nix-copy-closure", + NIX_COPY_CLOSURE, "--copy", "--to", "user@host", @@ -144,7 +153,7 @@ def run_wrapper_side_effect( ), call( [ - "nix-store", + NIX_STORE, "--realise", Path("/path/to/file"), "--add-root", @@ -189,7 +198,7 @@ def test_build_remote_flake( [ call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "eval", @@ -201,7 +210,7 @@ def test_build_remote_flake( ), call( [ - "nix-copy-closure", + NIX_COPY_CLOSURE, "--copy", "--to", "user@host", @@ -213,7 +222,7 @@ def test_build_remote_flake( ), call( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "build", @@ -239,7 +248,7 @@ def test_copy_closure(monkeypatch: MonkeyPatch) -> None: with patch(get_qualified_name(n.run_wrapper, n), autospec=True) as mock_run: n.copy_closure(closure, target_host) mock_run.assert_called_with( - ["nix-copy-closure", "--to", "user@target.host", closure], + [NIX_COPY_CLOSURE, "--to", "user@target.host", closure], extra_env={"NIX_SSHOPTS": " ".join(p.SSH_DEFAULT_OPTS)}, ) @@ -247,7 +256,7 @@ def test_copy_closure(monkeypatch: MonkeyPatch) -> None: with patch(get_qualified_name(n.run_wrapper, n), autospec=True) as mock_run: n.copy_closure(closure, None, build_host, {"copy_flag": True}) mock_run.assert_called_with( - ["nix-copy-closure", "--copy-flag", "--from", "user@build.host", closure], + [NIX_COPY_CLOSURE, "--copy-flag", "--from", "user@build.host", closure], extra_env={ "NIX_SSHOPTS": " ".join([*p.SSH_DEFAULT_OPTS, "--ssh build-opt"]) }, @@ -261,7 +270,7 @@ def test_copy_closure(monkeypatch: MonkeyPatch) -> None: n.copy_closure(closure, target_host, build_host, {"copy_flag": True}) mock_run.assert_called_with( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "copy", @@ -295,7 +304,7 @@ def test_edit_flake(mock_run: Mock) -> None: n.edit_flake(flake, {"commit_lock_file": True}) mock_run.assert_called_with( [ - "nix", + NIX, "--extra-experimental-features", "nix-command flakes", "edit", @@ -329,7 +338,7 @@ def test_get_build_image_variants(mock_run: Mock, tmp_path: Path) -> None: } mock_run.assert_called_with( [ - "nix-instantiate", + NIX_INSTANTIATE, "--eval", "--strict", "--json", @@ -352,7 +361,7 @@ def test_get_build_image_variants(mock_run: Mock, tmp_path: Path) -> None: } mock_run.assert_called_with( [ - "nix-instantiate", + NIX_INSTANTIATE, "--eval", "--strict", "--json", @@ -392,7 +401,7 @@ def test_get_build_image_variants_flake(mock_run: Mock) -> None: } mock_run.assert_called_with( [ - "nix", + NIX, "eval", "--json", "/flake.nix#myAttr.config.system.build.images", @@ -495,7 +504,7 @@ def test_get_generations_from_nix_env(tmp_path: Path) -> None: m.Generation(id=2084, current=True, timestamp="2024-11-07 23:54:17"), ] mock_run.assert_called_with( - ["nix-env", "-p", path, "--list-generations"], + [NIX_ENV, "-p", path, "--list-generations"], stdout=PIPE, remote=None, sudo=False, @@ -513,7 +522,7 @@ def test_get_generations_from_nix_env(tmp_path: Path) -> None: m.Generation(id=2084, current=True, timestamp="2024-11-07 23:54:17"), ] mock_run.assert_called_with( - ["nix-env", "-p", path, "--list-generations"], + [NIX_ENV, "-p", path, "--list-generations"], stdout=PIPE, remote=remote, sudo=True, @@ -565,11 +574,11 @@ def test_list_generations(mock_get_generations: Mock, tmp_path: Path) -> None: def test_repl(mock_run: Mock) -> None: n.repl(m.BuildAttr("", None), {"nix_flag": True}) mock_run.assert_called_with( - ["nix", "repl", "--file", "", "--nix-flag"] + [NIX, "repl", "--file", "", "--nix-flag"] ) n.repl(m.BuildAttr(Path("file.nix"), "myAttr")) - mock_run.assert_called_with(["nix", "repl", "--file", Path("file.nix"), "myAttr"]) + mock_run.assert_called_with([NIX, "repl", "--file", Path("file.nix"), "myAttr"]) @patch(get_qualified_name(n.run_wrapper, n), autospec=True) @@ -589,7 +598,7 @@ def test_rollback(mock_run: Mock, tmp_path: Path) -> None: assert n.rollback(profile, None, False) == profile.path mock_run.assert_called_with( - ["nix-env", "--rollback", "-p", path], + [NIX_ENV, "--rollback", "-p", path], remote=None, sudo=False, ) @@ -597,7 +606,7 @@ def test_rollback(mock_run: Mock, tmp_path: Path) -> None: target_host = m.Remote("user@localhost", [], None) assert n.rollback(profile, target_host, True) == profile.path mock_run.assert_called_with( - ["nix-env", "--rollback", "-p", path], + [NIX_ENV, "--rollback", "-p", path], remote=target_host, sudo=True, ) @@ -624,7 +633,7 @@ def test_rollback_temporary_profile(tmp_path: Path) -> None: ) mock_run.assert_called_with( [ - "nix-env", + NIX_ENV, "-p", path, "--list-generations", @@ -641,7 +650,7 @@ def test_rollback_temporary_profile(tmp_path: Path) -> None: ) mock_run.assert_called_with( [ - "nix-env", + NIX_ENV, "-p", path, "--list-generations", @@ -670,7 +679,7 @@ def test_set_profile(mock_run: Mock) -> None: ) mock_run.assert_called_with( - ["nix-env", "-p", profile_path, "--set", config_path], + [NIX_ENV, "-p", profile_path, "--set", config_path], remote=None, sudo=False, ) @@ -836,17 +845,15 @@ def test_upgrade_channels( n.upgrade_channels(all_channels=False, sudo=True) mock_run.assert_called_once_with( - ["nix-channel", "--update", "nixos"], check=False, sudo=True + [NIX_CHANNEL, "--update", "nixos"], check=False, sudo=True ) mock_geteuid.return_value = 0 n.upgrade_channels(all_channels=True, sudo=False) mock_run.assert_has_calls( [ - call(["nix-channel", "--update", "nixos"], check=False, sudo=False), - call( - ["nix-channel", "--update", "nixos-hardware"], check=False, sudo=False - ), - call(["nix-channel", "--update", "home-manager"], check=False, sudo=False), + call([NIX_CHANNEL, "--update", "nixos"], check=False, sudo=False), + call([NIX_CHANNEL, "--update", "nixos-hardware"], check=False, sudo=False), + call([NIX_CHANNEL, "--update", "home-manager"], check=False, sudo=False), ] ) From 525891d2b8392a611eb37536d0042dfdb6156b53 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 05:40:53 -0700 Subject: [PATCH 07/10] nixos-rebuild-ng: implement withShellFiles directly --- .../nixos-rebuild-ng/0001-replacements.patch | 10 +++---- .../nixos-rebuild-ng/0002-help-runs-man.patch | 30 +++++++++++++++++++ pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 4 +-- .../src/nixos_rebuild/__init__.py | 12 +++----- .../src/nixos_rebuild/constants.py | 1 - .../src/nixos_rebuild/help.py | 7 +++++ 6 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 pkgs/by-name/ni/nixos-rebuild-ng/0002-help-runs-man.patch create mode 100644 pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/help.py diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch index b832cc322d1f5..445d7da51a9d7 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch @@ -5,24 +5,22 @@ Subject: [PATCH] nixos-rebuild-ng: patch out actual replacements Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src` --- - nixos_rebuild/constants.py | 20 ++++++++++---------- + nixos_rebuild/constants.py | 18 +++++++++--------- pyproject.toml | 6 +++--- - 2 files changed, 13 insertions(+), 13 deletions(-) + 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nixos_rebuild/constants.py b/nixos_rebuild/constants.py -index 16164bbcd60b..e4423988c98a 100644 +index 9b2c04d88354..6c472320900d 100644 --- a/nixos_rebuild/constants.py +++ b/nixos_rebuild/constants.py -@@ -2,15 +2,15 @@ from typing import Final +@@ -2,14 +2,14 @@ from typing import Final # These are replaced in a patch for the actual derivation; what's below supports running the tool # out of the Nixpkgs repository directly. -EXECUTABLE: Final[str] = "nixos-rebuild-ng" -WITH_REEXEC: Final[bool] = True --WITH_SHELL_FILES: Final[bool] = True +EXECUTABLE: Final[str] = "@executable@" +WITH_REEXEC: Final[bool] = @withReexec@ -+WITH_SHELL_FILES: Final[bool] = @withShellFiles@ # These names are replaced with absolute paths to Nix in the store. -NIX: Final[str] = "nix" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0002-help-runs-man.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0002-help-runs-man.patch new file mode 100644 index 0000000000000..f697797c605d0 --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0002-help-runs-man.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Philip Taron +Date: Thu, 23 Oct 2025 05:46:07 -0700 +Subject: [PATCH] nixos-rebuild-ng: patch out print_help to use the man page + +Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src` +--- + nixos_rebuild/help.py | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/nixos_rebuild/help.py b/nixos_rebuild/help.py +index 7437550a37b8..dcb730e09ced 100644 +--- a/nixos_rebuild/help.py ++++ b/nixos_rebuild/help.py +@@ -1,7 +1,10 @@ + import argparse ++from subprocess import run + from typing import NoReturn + ++from .constants import EXECUTABLE ++ + + def print_help(parser: argparse.ArgumentParser) -> NoReturn: +- parser.print_help() +- parser.exit() ++ r = run(["man", "8", EXECUTABLE], check=False) ++ parser.exit(r.returncode) +-- +2.51.0 + diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index 95f570b78465f..70d37e4fd4077 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -49,7 +49,6 @@ buildPythonApplication { (replaceVars ./0001-replacements.patch { inherit executable version; withReexec = if withReexec then "True" else "False"; - withShellFiles = if withShellFiles then "True" else "False"; # Make sure that we use the Nix package we depend on, not the ambient Nix in the PATH. # @@ -65,7 +64,8 @@ buildPythonApplication { nix-instantiate = lib.getExe' nix "nix-instantiate"; nix-store = lib.getExe' nix "nix-store"; }) - ]; + ] + ++ lib.optional withShellFiles ./0002-help-runs-man.patch; postInstall = lib.optionalString withShellFiles '' scdoc < ${./nixos-rebuild.8.scd} > ${executable}.8 diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py index 6d7ad01baebdc..33596b0b740ff 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py @@ -1,11 +1,12 @@ import argparse import logging import sys -from subprocess import CalledProcessError, run +from subprocess import CalledProcessError from typing import Final, assert_never from . import nix, services -from .constants import EXECUTABLE, WITH_REEXEC, WITH_SHELL_FILES +from .constants import WITH_REEXEC +from .help import print_help from .models import Action, BuildAttr, Flake, Profile from .process import Remote from .utils import LogFormatter @@ -204,12 +205,7 @@ def parse_args( } if args.help or args.action is None: - if WITH_SHELL_FILES: - r = run(["man", "8", EXECUTABLE], check=False) - parser.exit(r.returncode) - else: - parser.print_help() - parser.exit() + print_help(parser) def parser_warn(msg: str) -> None: print(f"{parser.prog}: warning: {msg}", file=sys.stderr) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py index 16164bbcd60bb..9b2c04d883545 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py @@ -4,7 +4,6 @@ # out of the Nixpkgs repository directly. EXECUTABLE: Final[str] = "nixos-rebuild-ng" WITH_REEXEC: Final[bool] = True -WITH_SHELL_FILES: Final[bool] = True # These names are replaced with absolute paths to Nix in the store. NIX: Final[str] = "nix" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/help.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/help.py new file mode 100644 index 0000000000000..7437550a37b8c --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/help.py @@ -0,0 +1,7 @@ +import argparse +from typing import NoReturn + + +def print_help(parser: argparse.ArgumentParser) -> NoReturn: + parser.print_help() + parser.exit() From 079cf93d789ec61a74574ae536d6c64814c809bb Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 06:00:40 -0700 Subject: [PATCH 08/10] nixos-rebuild-ng: implement withReexec using makeWrapperArgs rather than a patch --- .../nixos-rebuild-ng/0001-replacements.patch | 18 ++++++++---------- pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 7 +++---- .../src/nixos_rebuild/__init__.py | 16 +++++++--------- .../src/nixos_rebuild/constants.py | 7 +++---- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch index 445d7da51a9d7..3c59e4aac9ca1 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch @@ -3,26 +3,24 @@ From: Philip Taron Date: Wed, 22 Oct 2025 17:02:15 -0700 Subject: [PATCH] nixos-rebuild-ng: patch out actual replacements -Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src` +Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src HEAD^1` --- - nixos_rebuild/constants.py | 18 +++++++++--------- + nixos_rebuild/constants.py | 16 ++++++++-------- pyproject.toml | 6 +++--- - 2 files changed, 12 insertions(+), 12 deletions(-) + 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/nixos_rebuild/constants.py b/nixos_rebuild/constants.py -index 9b2c04d88354..6c472320900d 100644 +index ae38b3ef297b..a33be661a17e 100644 --- a/nixos_rebuild/constants.py +++ b/nixos_rebuild/constants.py -@@ -2,14 +2,14 @@ from typing import Final +@@ -2,13 +2,13 @@ from typing import Final - # These are replaced in a patch for the actual derivation; what's below supports running the tool - # out of the Nixpkgs repository directly. + # The name of this executable, for purposes of replacing `nixos-rebuild`. + # The derivation replaces this using a patch file. -EXECUTABLE: Final[str] = "nixos-rebuild-ng" --WITH_REEXEC: Final[bool] = True +EXECUTABLE: Final[str] = "@executable@" -+WITH_REEXEC: Final[bool] = @withReexec@ - # These names are replaced with absolute paths to Nix in the store. + # These names are replaced with absolute paths to Nix in the store in the derivation. -NIX: Final[str] = "nix" -NIX_BUILD: Final[str] = "nix-build" -NIX_CHANNEL: Final[str] = "nix-channel" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index 70d37e4fd4077..c97299ec56a60 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -48,7 +48,6 @@ buildPythonApplication { patches = [ (replaceVars ./0001-replacements.patch { inherit executable version; - withReexec = if withReexec then "True" else "False"; # Make sure that we use the Nix package we depend on, not the ambient Nix in the PATH. # @@ -80,9 +79,9 @@ buildPythonApplication { pytestFlags = [ "-vv" ]; - makeWrapperArgs = lib.optionals (withTmpdir != null) [ - "--set TMPDIR ${withTmpdir}" - ]; + makeWrapperArgs = + lib.optional (!withReexec) "--set NIXOS_REBUILD_REEXEC_ENV 1" + ++ lib.optional (withTmpdir != null) "--set TMPDIR ${withTmpdir}"; passthru = let diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py index 33596b0b740ff..b5fa1cb2170a4 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py @@ -5,7 +5,6 @@ from typing import Final, assert_never from . import nix, services -from .constants import WITH_REEXEC from .help import print_help from .models import Action, BuildAttr, Flake, Profile from .process import Remote @@ -276,16 +275,15 @@ def execute(argv: list[str]) -> None: nix.upgrade_channels(args.upgrade_all, args.sudo) action = Action(args.action) - # Only run shell scripts from the Nixpkgs tree if the action is - # "switch", "boot", or "test". With other actions (such as "build"), - # the user may reasonably expect that no code from the Nixpkgs tree is - # executed, so it's safe to run nixos-rebuild against a potentially - # untrusted tree. + + # Only run shell scripts from the Nixpkgs tree if the action is "switch", "boot", or "test". + # With other actions (such as "build"), the user may reasonably expect that no code from the + # Nixpkgs tree is executed, so it's safe to run nixos-rebuild against a potentially untrusted + # tree. can_run = action in (Action.SWITCH, Action.BOOT, Action.TEST) - # Re-exec to a newer version of the script before building to ensure we get - # the latest fixes - if WITH_REEXEC and can_run and not args.no_reexec: + # Re-exec to a newer version of the script before building to ensure we get the latest fixes. + if can_run and not args.no_reexec: services.reexec(argv, args, build_flags, flake_build_flags) profile = Profile.from_arg(args.profile_name) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py index 9b2c04d883545..ae38b3ef297b6 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py @@ -1,11 +1,10 @@ from typing import Final -# These are replaced in a patch for the actual derivation; what's below supports running the tool -# out of the Nixpkgs repository directly. +# The name of this executable, for purposes of replacing `nixos-rebuild`. +# The derivation replaces this using a patch file. EXECUTABLE: Final[str] = "nixos-rebuild-ng" -WITH_REEXEC: Final[bool] = True -# These names are replaced with absolute paths to Nix in the store. +# These names are replaced with absolute paths to Nix in the store in the derivation. NIX: Final[str] = "nix" NIX_BUILD: Final[str] = "nix-build" NIX_CHANNEL: Final[str] = "nix-channel" From e9be87fef8d3e4e861f665fa1dc18a2f179f538d Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 15:39:58 -0700 Subject: [PATCH 09/10] nixos-rebuild-ng: prepare for using `nom build` `nom` doesn't accept `--extra-experimental-features` before `nom build`; since `nix` accepts it anywhere in the command line, put it after the build. The tests also need to be updated for the `NIX_OR_NOM` constant. --- .../nixos-rebuild-ng/0001-replacements.patch | 11 +++-- pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 1 + .../src/nixos_rebuild/constants.py | 2 + .../nixos-rebuild-ng/src/nixos_rebuild/nix.py | 9 ++-- .../nixos-rebuild-ng/src/tests/test_main.py | 41 ++++++++++--------- .../ni/nixos-rebuild-ng/src/tests/test_nix.py | 9 ++-- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch index 3c59e4aac9ca1..41f12ae69caa6 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch +++ b/pkgs/by-name/ni/nixos-rebuild-ng/0001-replacements.patch @@ -5,15 +5,15 @@ Subject: [PATCH] nixos-rebuild-ng: patch out actual replacements Produced with `git format-patch -1 --zero-commit --stdout --relative=pkgs/by-name/ni/nixos-rebuild-ng/src HEAD^1` --- - nixos_rebuild/constants.py | 16 ++++++++-------- + nixos_rebuild/constants.py | 18 +++++++++--------- pyproject.toml | 6 +++--- - 2 files changed, 11 insertions(+), 11 deletions(-) + 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nixos_rebuild/constants.py b/nixos_rebuild/constants.py -index ae38b3ef297b..a33be661a17e 100644 +index f79922ea5c87..ee4fc0c49373 100644 --- a/nixos_rebuild/constants.py +++ b/nixos_rebuild/constants.py -@@ -2,13 +2,13 @@ from typing import Final +@@ -2,15 +2,15 @@ from typing import Final # The name of this executable, for purposes of replacing `nixos-rebuild`. # The derivation replaces this using a patch file. @@ -21,7 +21,9 @@ index ae38b3ef297b..a33be661a17e 100644 +EXECUTABLE: Final[str] = "@executable@" # These names are replaced with absolute paths to Nix in the store in the derivation. + # Some of these names could be either `nix` or `nom`, and are called out as such. -NIX: Final[str] = "nix" +-NIX_OR_NOM: Final[str] = "nix" -NIX_BUILD: Final[str] = "nix-build" -NIX_CHANNEL: Final[str] = "nix-channel" -NIX_COPY_CLOSURE: Final[str] = "nix-copy-closure" @@ -29,6 +31,7 @@ index ae38b3ef297b..a33be661a17e 100644 -NIX_INSTANTIATE: Final[str] = "nix-instantiate" -NIX_STORE: Final[str] = "nix-store" +NIX: Final[str] = "@nix@" ++NIX_OR_NOM: Final[str] = "@nix-or-nom@" +NIX_BUILD: Final[str] = "@nix-build@" +NIX_CHANNEL: Final[str] = "@nix-channel@" +NIX_COPY_CLOSURE: Final[str] = "@nix-copy-closure@" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index c97299ec56a60..9f30205e76d2c 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -56,6 +56,7 @@ buildPythonApplication { # package in her PATH, then we would silently downgrade the whole system to be i686 NixOS on # the next reboot. nix = lib.getExe' nix "nix"; + nix-or-nom = lib.getExe' nix "nix"; nix-build = lib.getExe' nix "nix-build"; nix-channel = lib.getExe' nix "nix-channel"; nix-copy-closure = lib.getExe' nix "nix-copy-closure"; diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py index ae38b3ef297b6..f79922ea5c876 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/constants.py @@ -5,7 +5,9 @@ EXECUTABLE: Final[str] = "nixos-rebuild-ng" # These names are replaced with absolute paths to Nix in the store in the derivation. +# Some of these names could be either `nix` or `nom`, and are called out as such. NIX: Final[str] = "nix" +NIX_OR_NOM: Final[str] = "nix" NIX_BUILD: Final[str] = "nix-build" NIX_CHANNEL: Final[str] = "nix-channel" NIX_COPY_CLOSURE: Final[str] = "nix-copy-closure" diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py index 1821dc263877a..b3972f4b58245 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py @@ -20,6 +20,7 @@ NIX_COPY_CLOSURE, NIX_ENV, NIX_INSTANTIATE, + NIX_OR_NOM, NIX_STORE, ) from .models import ( @@ -86,9 +87,9 @@ def build_flake( Returns the built attribute as path. """ run_args = [ - NIX, - *FLAKE_FLAGS, + NIX_OR_NOM, "build", + *FLAKE_FLAGS, "--print-out-paths", flake.to_attr(attr), *dict_to_flags(flake_build_flags), @@ -174,9 +175,9 @@ def build_remote_flake( copy_closure(drv, to_host=build_host, from_host=None, copy_flags=copy_flags) r = run_wrapper( [ - NIX, - *FLAKE_FLAGS, + NIX_OR_NOM, "build", + *FLAKE_FLAGS, f"{drv}^*", "--print-out-paths", *dict_to_flags(flake_build_flags), diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py index fe2da287ba880..487a7ea296f66 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py @@ -16,6 +16,7 @@ NIX_COPY_CLOSURE, NIX_ENV, NIX_INSTANTIATE, + NIX_OR_NOM, NIX_STORE, ) @@ -286,16 +287,16 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == NIX and "eval" in args: + if args[0] not in (NIX, NIX_OR_NOM): + return CompletedProcess([], 0) + elif "eval" in args: return CompletedProcess( [], 0, '"nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd"', ) - elif args[0] == NIX: - return CompletedProcess([], 0, str(config_path)) else: - return CompletedProcess([], 0) + return CompletedProcess([], 0, str(config_path)) mock_run.side_effect = run_side_effect @@ -328,10 +329,10 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: ), call( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "--print-out-paths", '/path/to/config#nixosConfigurations."hostname".config.system.build.images.azure', ], @@ -365,7 +366,7 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == NIX: + if args[0] in (NIX, NIX_OR_NOM): return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -394,10 +395,10 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "--print-out-paths", '/path/to/config#nixosConfigurations."hostname".config.system.build.toplevel', "-v", @@ -681,7 +682,7 @@ def test_execute_nix_switch_flake_target_host( config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == NIX: + if args[0] in (NIX, NIX_OR_NOM): return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -706,10 +707,10 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "--print-out-paths", '/path/to/config#nixosConfigurations."hostname".config.system.build.toplevel', "--no-link", @@ -790,7 +791,7 @@ def test_execute_nix_switch_flake_build_host( def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: if args[0] == NIX and "eval" in args: return CompletedProcess([], 0, str(config_path)) - elif args[0] == "ssh" and NIX in args: + elif args[0] == "ssh" and (NIX in args or NIX_OR_NOM in args): return CompletedProcess([], 0, str(config_path)) else: return CompletedProcess([], 0) @@ -836,10 +837,10 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: *nr.process.SSH_DEFAULT_OPTS, "user@localhost", "--", - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "'nix-command flakes'", - "build", f"'{config_path}^*'", "--print-out-paths", "--no-link", @@ -1045,10 +1046,10 @@ def test_execute_build_dry_run_build_and_target_remote( *nr.process.SSH_DEFAULT_OPTS, "user@build-host", "--", - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "'nix-command flakes'", - "build", f"'{config_path}^*'", "--print-out-paths", "--dry-run", @@ -1067,7 +1068,7 @@ def test_execute_test_flake(mock_run: Mock, tmp_path: Path) -> None: config_path.touch() def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: - if args[0] == NIX: + if args[0] in (NIX, NIX_OR_NOM): return CompletedProcess([], 0, str(config_path)) elif args[0] == "test": return CompletedProcess([], 1) @@ -1085,10 +1086,10 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]: [ call( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "--print-out-paths", 'github:user/repo#nixosConfigurations."hostname".config.system.build.toplevel', "--no-link", diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py index 4f12e5bb1eb7f..071e253ed02c4 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py @@ -18,6 +18,7 @@ NIX_COPY_CLOSURE, NIX_ENV, NIX_INSTANTIATE, + NIX_OR_NOM, NIX_STORE, ) @@ -72,10 +73,10 @@ def test_build_flake(mock_run: Mock, monkeypatch: MonkeyPatch, tmpdir: Path) -> ) == Path("/path/to/file") mock_run.assert_called_with( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "--print-out-paths", '/flake.nix#nixosConfigurations."hostname".config.system.build.toplevel', "--no-link", @@ -222,10 +223,10 @@ def test_build_remote_flake( ), call( [ - NIX, + NIX_OR_NOM, + "build", "--extra-experimental-features", "nix-command flakes", - "build", "/path/to/file^*", "--print-out-paths", "--build", From d0e571d973c8e39d4ca0a465796c8a7c0efe3b84 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Thu, 23 Oct 2025 06:44:31 -0700 Subject: [PATCH 10/10] nixos: introduce system.rebuild.enableNom option This uses `nix-output-monitor` to produce a much better looking `nixos-rebuild` experience. --- nixos/modules/installer/tools/tools.nix | 9 +++++++++ nixos/tests/all-tests.nix | 1 + pkgs/by-name/ni/nixos-rebuild-ng/package.nix | 6 ++++++ pkgs/by-name/ni/nixos-rebuild-ng/python.nix | 20 ++++++++++++++++++-- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index bf8a27419ae21..5e5c3bc3ea2c9 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -72,6 +72,7 @@ let nixos-rebuild-ng = pkgs.nixos-rebuild-ng.override { nix = config.nix.package; withNgSuffix = false; + withNom = config.system.rebuild.enableNom; withReexec = true; }; @@ -287,6 +288,14 @@ in ''; }; + options.system.rebuild.enableNom = lib.mkEnableOption "" // { + default = false; + description = '' + Whether to use ‘nix-output-monitor’ in place of ‘nix’ when rebuilding. + This produces a more aesthetically pleasing terminal experience. + ''; + }; + imports = let mkToolModule = diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3af4002a6aa2e..7f8fb7f1c2b7f 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1059,6 +1059,7 @@ in nixos-rebuild-specialisations-ng = runTestOn [ "x86_64-linux" ] { imports = [ ./nixos-rebuild-specialisations.nix ]; _module.args.withNg = true; + _module.args.withNom = true; }; nixos-rebuild-target-host = runTest { imports = [ ./nixos-rebuild-target-host.nix ]; diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix index 8add24cc58cb0..8db38e7326490 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/package.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/package.nix @@ -1,5 +1,7 @@ { + lib, stdenv, + makeBinaryWrapper, python3Packages, # These are required to be passed through. @@ -7,12 +9,14 @@ lixPackageSets, mkShell, nix, + nix-output-monitor, nixVersions, nixos-rebuild-ng, nixosTests, scdoc, withNgSuffix ? true, + withNom ? false, withReexec ? false, withShellFiles ? true, # Very long tmp dirs lead to "too long for Unix domain socket" @@ -26,11 +30,13 @@ python3Packages.callPackage ./python.nix { lixPackageSets mkShell nix + nix-output-monitor nixVersions nixos-rebuild-ng nixosTests scdoc withNgSuffix + withNom withReexec withShellFiles withTmpdir diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix index 9f30205e76d2c..152a0b5105267 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/python.nix +++ b/pkgs/by-name/ni/nixos-rebuild-ng/python.nix @@ -13,6 +13,7 @@ lixPackageSets, mkShell, nix, + nix-output-monitor, nixVersions, nixos-rebuild-ng, nixosTests, @@ -21,6 +22,7 @@ # Override interface, required to be passed in from `./package.nix`. withNgSuffix, + withNom, withReexec, withShellFiles, withTmpdir, @@ -28,6 +30,15 @@ let executable = if withNgSuffix then "nixos-rebuild-ng" else "nixos-rebuild"; version = lib.trivial.release; + + # If we're using `nix-output-monitor`, make sure it's pinned to the right version of Nix. + nix-output-monitor-pinned = nix-output-monitor.override { + withPinnedNix = true; + inherit nix; + }; + + maybeNom = if withNom then nix-output-monitor-pinned else nix; + nix-or-nom = if withNom then "nom" else "nix"; in buildPythonApplication { pname = "nixos-rebuild-ng"; @@ -50,14 +61,15 @@ buildPythonApplication { inherit executable version; # Make sure that we use the Nix package we depend on, not the ambient Nix in the PATH. + # If we've requested to use `nom`, use that for `nix` and `nix-build` commands. # # This is important, because NixOS defaults the architecture of the rebuilt system to the # architecture of the nix-* binaries used. So if on an amd64 system the user has an i686 Nix # package in her PATH, then we would silently downgrade the whole system to be i686 NixOS on # the next reboot. nix = lib.getExe' nix "nix"; - nix-or-nom = lib.getExe' nix "nix"; - nix-build = lib.getExe' nix "nix-build"; + nix-or-nom = lib.getExe' maybeNom "${nix-or-nom}"; + nix-build = lib.getExe' maybeNom "${nix-or-nom}-build"; nix-channel = lib.getExe' nix "nix-channel"; nix-copy-closure = lib.getExe' nix "nix-copy-closure"; nix-env = lib.getExe' nix "nix-env"; @@ -106,6 +118,10 @@ buildPythonApplication { }; tests = { + with_nom = nixos-rebuild-ng.override { + withNom = true; + }; + with_reexec = nixos-rebuild-ng.override { withReexec = true; withNgSuffix = false;