From 0dce56f3f52ee767c6d7aa816675d33c1c12a630 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 19 Jun 2025 20:29:23 +0100 Subject: [PATCH 1/5] nixos-rebuild-ng: validate NixOS configuration path When `path://` or `git+file://` protocol is used in Flake mode (that is the most common case since we normalize the paths, see PR #375493) and the current working directory in a symlink pointing base store path to the Nix store (e.g., /run/opengl-driver/lib), there is a nasty bug where Nix resolves the path as the Nix store path of the current derivation instead of the target derivation. Since we blindly activate this path, this can corrupt the installation and break some other activation scripts, like `systemd-boot-builder.py`. While it is possible to recover this situation using `nix-env -p /nix/var/nix/profiles/system --delete-generations old`, this is far from ideal. This commit solves it by validating that the resolved NixOS configuration path includes at least `$out/nixos-version`. I am not sure if this is going to break some cases so there is a escape hatch in the form of the environment variable `NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM`, but in general it looks safe. --- .../src/nixos_rebuild/__init__.py | 26 ++++++++++ .../nixos-rebuild-ng/src/tests/test_main.py | 50 ++++++++++++++++--- 2 files changed, 69 insertions(+), 7 deletions(-) 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 838b8913abb3..fb68b219c1f9 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,6 +5,7 @@ import os import sys from pathlib import Path from subprocess import CalledProcessError, run +from textwrap import dedent from typing import Final, assert_never from . import nix, tmpdir @@ -329,6 +330,30 @@ def reexec( os.execve(current, argv, os.environ | {"_NIXOS_REBUILD_REEXEC": "1"}) +def validate_nixos_config(path_to_config: Path) -> None: + if not (path_to_config / "nixos-version").exists() and not os.environ.get( + "NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM" + ): + msg = dedent( + # the lowercase for the first letter below is proposital + f""" + your NixOS configuration path seems to be missing essential files. + To avoid corrupting your current NixOS installation, the activation will abort. + + This could be caused by Nix bug: https://github.com/NixOS/nix/issues/13367. + This is the evaluated NixOS configuration path: {path_to_config}. + Change the directory to somewhere else (e.g., `cd $HOME`) before trying again. + + If you think this is a mistake, you can set the environment variable + NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM to 1 + and re-run the command to continue. + Please open an issue if this is the case. + """ + ).strip() + logger.error(msg) + sys.exit(1) + + def execute(argv: list[str]) -> None: args, args_groups = parse_args(argv) @@ -488,6 +513,7 @@ def execute(argv: list[str]) -> None: copy_flags=copy_flags, ) if action in (Action.SWITCH, Action.BOOT): + validate_nixos_config(path_to_config) nix.set_profile( profile, path_to_config, 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 a66d70498c5f..345c8bd32f79 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 @@ -213,7 +213,11 @@ def test_reexec_flake( ) -@patch.dict(os.environ, {}, clear=True) +@patch.dict( + os.environ, + {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"}, + clear=True, +) @patch("subprocess.run", autospec=True) def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None: nixpkgs_path = tmp_path / "nixpkgs" @@ -291,7 +295,15 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None: "boot", ], check=True, - **(DEFAULT_RUN_KWARGS | {"env": {"NIXOS_INSTALL_BOOTLOADER": "0"}}), + **( + DEFAULT_RUN_KWARGS + | { + "env": { + "NIXOS_INSTALL_BOOTLOADER": "0", + "NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1", + } + } + ), ), ] ) @@ -421,7 +433,11 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None: ) -@patch.dict(os.environ, {}, clear=True) +@patch.dict( + os.environ, + {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"}, + clear=True, +) @patch("subprocess.run", autospec=True) def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None: config_path = tmp_path / "test" @@ -498,13 +514,25 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None: "switch", ], check=True, - **(DEFAULT_RUN_KWARGS | {"env": {"NIXOS_INSTALL_BOOTLOADER": "1"}}), + **( + DEFAULT_RUN_KWARGS + | { + "env": { + "NIXOS_INSTALL_BOOTLOADER": "1", + "NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1", + } + } + ), ), ] ) -@patch.dict(os.environ, {}, clear=True) +@patch.dict( + os.environ, + {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"}, + clear=True, +) @patch("subprocess.run", autospec=True) @patch("uuid.uuid4", autospec=True) @patch(get_qualified_name(nr.cleanup_ssh), autospec=True) @@ -714,7 +742,11 @@ def test_execute_nix_switch_build_target_host( ) -@patch.dict(os.environ, {}, clear=True) +@patch.dict( + os.environ, + {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"}, + clear=True, +) @patch("subprocess.run", autospec=True) @patch(get_qualified_name(nr.cleanup_ssh), autospec=True) def test_execute_nix_switch_flake_target_host( @@ -817,7 +849,11 @@ def test_execute_nix_switch_flake_target_host( ) -@patch.dict(os.environ, {}, clear=True) +@patch.dict( + os.environ, + {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"}, + clear=True, +) @patch("subprocess.run", autospec=True) @patch(get_qualified_name(nr.cleanup_ssh), autospec=True) def test_execute_nix_switch_flake_build_host( From 8481fa781e03ca06d084f353a9ce2ff355a9e95f Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 19 Jun 2025 20:48:48 +0100 Subject: [PATCH 2/5] nixos-rebuild-ng: simplify `Flake.parse()` code --- .../src/nixos_rebuild/models.py | 45 +++++++++---------- .../nixos-rebuild-ng/src/tests/test_models.py | 25 ++++++++--- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py index 0d67bc81aed9..f8439461b6bd 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py @@ -4,7 +4,7 @@ import subprocess from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, Callable, ClassVar, Self, TypedDict, override +from typing import Any, ClassVar, Self, TypedDict, override from .process import Remote, run_wrapper @@ -100,6 +100,20 @@ def discover_closest_flake(location: Path) -> Path | None: return None +def get_hostname(target_host: Remote | None) -> str | None: + if target_host: + try: + return run_wrapper( + ["uname", "-n"], + capture_output=True, + remote=target_host, + ).stdout.strip() + except (AttributeError, subprocess.CalledProcessError): + return None + else: + return platform.node() + + @dataclass(frozen=True) class Flake: path: Path | str @@ -114,15 +128,13 @@ class Flake: return f"{self.path}#{self.attr}" @classmethod - def parse( - cls, - flake_str: str, - hostname_fn: Callable[[], str | None] = lambda: None, - ) -> Self: + def parse(cls, flake_str: str, target_host: Remote | None = None) -> Self: m = cls._re.match(flake_str) assert m is not None, f"got no matches for {flake_str}" attr = m.group("attr") - nixos_attr = f'nixosConfigurations."{attr or hostname_fn() or "default"}"' + nixos_attr = ( + f'nixosConfigurations."{attr or get_hostname(target_host) or "default"}"' + ) path_str = m.group("path") if ":" in path_str: return cls(path_str, nixos_attr) @@ -143,24 +155,11 @@ class Flake: @classmethod def from_arg(cls, flake_arg: Any, target_host: Remote | None) -> Self | None: - def get_hostname() -> str | None: - if target_host: - try: - return run_wrapper( - ["uname", "-n"], - stdout=subprocess.PIPE, - remote=target_host, - ).stdout.strip() - except (AttributeError, subprocess.CalledProcessError): - return None - else: - return platform.node() - match flake_arg: case str(s): - return cls.parse(s, get_hostname) + return cls.parse(s, target_host) case True: - return cls.parse(".", get_hostname) + return cls.parse(".", target_host) case False: return None case _: @@ -169,7 +168,7 @@ class Flake: if default_path.exists(): # It can be a symlink to the actual flake. default_path = default_path.resolve() - return cls.parse(str(default_path.parent), get_hostname) + return cls.parse(str(default_path.parent), target_host) else: return None diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_models.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_models.py index 2cc450e00a36..1922b4682cbf 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_models.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_models.py @@ -30,16 +30,23 @@ def test_build_attr_to_attr() -> None: ) -def test_flake_parse(tmpdir: Path, monkeypatch: MonkeyPatch) -> None: +@patch("platform.node", autospec=True, return_value="hostname") +def test_flake_parse(mock_node: Mock, tmpdir: Path, monkeypatch: MonkeyPatch) -> None: assert m.Flake.parse("/path/to/flake#attr") == m.Flake( Path("/path/to/flake"), 'nixosConfigurations."attr"' ) - assert m.Flake.parse("/path/ to /flake", lambda: "hostname") == m.Flake( + assert m.Flake.parse("/path/ to /flake") == m.Flake( Path("/path/ to /flake"), 'nixosConfigurations."hostname"' ) - assert m.Flake.parse("/path/to/flake", lambda: "hostname") == m.Flake( - Path("/path/to/flake"), 'nixosConfigurations."hostname"' - ) + with patch( + get_qualified_name(m.run_wrapper, m), + autospec=True, + return_value=subprocess.CompletedProcess([], 0, stdout="remote\n"), + ): + target_host = m.Remote("target@remote", [], None) + assert m.Flake.parse("/path/to/flake", target_host) == m.Flake( + Path("/path/to/flake"), 'nixosConfigurations."remote"' + ) # change directory to tmpdir with monkeypatch.context() as patch_context: patch_context.chdir(tmpdir) @@ -49,10 +56,16 @@ def test_flake_parse(tmpdir: Path, monkeypatch: MonkeyPatch) -> None: assert m.Flake.parse("#attr") == m.Flake( Path("."), 'nixosConfigurations."attr"' ) - assert m.Flake.parse(".") == m.Flake(Path("."), 'nixosConfigurations."default"') + assert m.Flake.parse(".") == m.Flake( + Path("."), 'nixosConfigurations."hostname"' + ) assert m.Flake.parse("path:/to/flake#attr") == m.Flake( "path:/to/flake", 'nixosConfigurations."attr"' ) + + # from here on we should return "default" + mock_node.return_value = None + assert m.Flake.parse("github:user/repo/branch") == m.Flake( "github:user/repo/branch", 'nixosConfigurations."default"' ) From e2dbb9de3392c12142c827127f405b84507dfde9 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 19 Jun 2025 22:04:27 +0100 Subject: [PATCH 3/5] nixos-rebuild-ng: add string concatenation linter --- .../src/nixos_rebuild/__init__.py | 12 ++++++------ .../nixos-rebuild-ng/src/nixos_rebuild/nix.py | 2 +- .../src/nixos_rebuild/process.py | 6 +++--- .../ni/nixos-rebuild-ng/src/pyproject.toml | 19 ++++++++++--------- 4 files changed, 20 insertions(+), 19 deletions(-) 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 fb68b219c1f9..2e3a321fbcd4 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 @@ -100,7 +100,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa "--attr", "-A", help="Enable and build the NixOS system from nix file and use the " - + "specified attribute path from file specified by the --file option", + "specified attribute path from file specified by the --file option", ) main_parser.add_argument( "--flake", @@ -118,7 +118,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa "--install-bootloader", action="store_true", help="Causes the boot loader to be (re)installed on the device specified " - + "by the relevant configuration options", + "by the relevant configuration options", ) main_parser.add_argument( "--install-grub", @@ -143,7 +143,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa "--upgrade", action="store_true", help="Update the root user's channel named 'nixos' before rebuilding " - + "the system and channels which have a file named '.update-on-nixos-rebuild'", + "the system and channels which have a file named '.update-on-nixos-rebuild'", ) main_parser.add_argument( "--upgrade-all", @@ -187,7 +187,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa main_parser.add_argument( "--image-variant", help="Selects an image variant to build from the " - + "config.system.build.images attribute of the given configuration", + "config.system.build.images attribute of the given configuration", ) main_parser.add_argument("action", choices=Action.values(), nargs="?") @@ -322,7 +322,7 @@ def reexec( # - Exec format error (e.g.: another OS/CPU arch) logger.warning( "could not re-exec in a newer version of nixos-rebuild, " - + "using current version", + "using current version", exc_info=logger.isEnabledFor(logging.DEBUG), ) # We already run clean-up, let's re-exec in the current version @@ -422,7 +422,7 @@ def execute(argv: list[str]) -> None: if args.image_variant not in variants: raise NRError( "please specify one of the following " - + "supported image variants via --image-variant:\n" + "supported image variants via --image-variant:\n" + "\n".join(f"- {v}" for v in variants) ) 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 3be610b5ddbd..a55bfd508e37 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 @@ -652,7 +652,7 @@ def switch_to_configuration( if r.returncode: logger.debug( "skipping systemd-run to switch configuration since systemd is " - + "not working in target host" + "not working in target host" ) cmd = [] diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/process.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/process.py index 462c4178e8f7..1f57111a72ef 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/process.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/process.py @@ -55,12 +55,12 @@ class Remote: if o in ["-t", "-tt", "RequestTTY=yes", "RequestTTY=force"]: logger.warning( f"detected option '{o}' in NIX_SSHOPTS. SSH's TTY may " - + "cause issues, it is recommended to remove this option" + "cause issues, it is recommended to remove this option" ) if not ask_sudo_password: logger.warning( "if you want to prompt for sudo password use " - + "'--ask-sudo-password' option instead" + "'--ask-sudo-password' option instead" ) @@ -161,7 +161,7 @@ def run_wrapper( if sudo and remote and remote.sudo_password is None: logger.error( "while running command with remote sudo, did you forget to use " - + "--ask-sudo-password?" + "--ask-sudo-password?" ) raise diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml b/pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml index 7874c217468d..3eb78f58e032 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml @@ -39,37 +39,38 @@ ignore_missing_imports = true [tool.ruff.lint] extend-select = [ - # Enforce type annotations + # enforce type annotations "ANN", # don't shadow built-in names "A", - # Better list/set/dict comprehensions + # better list/set/dict comprehensions "C4", - # Check for debugger statements + # check for debugger statements "T10", # ensure imports are sorted "I", - # Automatically upgrade syntax for newer versions + # automatically upgrade syntax for newer versions "UP", # detect common sources of bugs "B", - # Ruff specific rules + # ruff specific rules "RUF", # require `check` argument for `subprocess.run` "PLW1510", # check for needless exception names in raise statements "TRY201", - # Pythonic naming conventions + # pythonic naming conventions "N", + # string concatenation rules + "ISC001", + "ISC002", + "ISC003", ] ignore = [ # allow Any type "ANN401" ] -[tool.ruff.lint.per-file-ignores] -"tests/" = ["FA102"] - [tool.pytest.ini_options] pythonpath = ["."] addopts = "--import-mode=importlib" From 3f58a116563b6f6becd5a1260ca6796b4be469c9 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 19 Jun 2025 22:17:53 +0100 Subject: [PATCH 4/5] nixos-rebuild-ng: move validate_image_variant() outside execute() --- .../src/nixos_rebuild/__init__.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) 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 2e3a321fbcd4..761af4b8b16d 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 @@ -330,6 +330,14 @@ def reexec( os.execve(current, argv, os.environ | {"_NIXOS_REBUILD_REEXEC": "1"}) +def validate_image_variant(image_variant: str, variants: ImageVariants) -> None: + if image_variant not in variants: + raise NRError( + "please specify one of the following supported image variants via " + "--image-variant:\n" + "\n".join(f"- {v}" for v in variants) + ) + + def validate_nixos_config(path_to_config: Path) -> None: if not (path_to_config / "nixos-version").exists() and not os.environ.get( "NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM" @@ -350,8 +358,7 @@ def validate_nixos_config(path_to_config: Path) -> None: Please open an issue if this is the case. """ ).strip() - logger.error(msg) - sys.exit(1) + raise NRError(msg) def execute(argv: list[str]) -> None: @@ -418,28 +425,20 @@ def execute(argv: list[str]) -> None: no_link = action in (Action.SWITCH, Action.BOOT) rollback = bool(args.rollback) - def validate_image_variant(variants: ImageVariants) -> None: - if args.image_variant not in variants: - raise NRError( - "please specify one of the following " - "supported image variants via --image-variant:\n" - + "\n".join(f"- {v}" for v in variants) - ) - match action: case Action.BUILD_IMAGE if flake: variants = nix.get_build_image_variants_flake( flake, eval_flags=flake_common_flags, ) - validate_image_variant(variants) + validate_image_variant(args.image_variant, variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_IMAGE: variants = nix.get_build_image_variants( build_attr, instantiate_flags=common_flags, ) - validate_image_variant(variants) + validate_image_variant(args.image_variant, variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_VM: attr = "config.system.build.vm" From e364976238fcc10cb7005e2b12e71c4b0a9691a4 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Thu, 19 Jun 2025 22:39:02 +0100 Subject: [PATCH 5/5] nixos-rebuild-ng: NRError -> NixOSRebuildError --- .../nixos-rebuild-ng/src/nixos_rebuild/__init__.py | 12 +++++++----- .../nixos-rebuild-ng/src/nixos_rebuild/models.py | 2 +- .../ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py | 14 +++++++------- .../ni/nixos-rebuild-ng/src/tests/test_nix.py | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) 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 761af4b8b16d..f89eddab6183 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 @@ -10,7 +10,7 @@ from typing import Final, assert_never from . import nix, tmpdir from .constants import EXECUTABLE, WITH_NIX_2_18, WITH_REEXEC, WITH_SHELL_FILES -from .models import Action, BuildAttr, Flake, ImageVariants, NRError, Profile +from .models import Action, BuildAttr, Flake, ImageVariants, NixOSRebuildError, Profile from .process import Remote, cleanup_ssh from .utils import Args, LogFormatter, tabulate @@ -332,7 +332,7 @@ def reexec( def validate_image_variant(image_variant: str, variants: ImageVariants) -> None: if image_variant not in variants: - raise NRError( + raise NixOSRebuildError( "please specify one of the following supported image variants via " "--image-variant:\n" + "\n".join(f"- {v}" for v in variants) ) @@ -358,7 +358,7 @@ def validate_nixos_config(path_to_config: Path) -> None: Please open an issue if this is the case. """ ).strip() - raise NRError(msg) + raise NixOSRebuildError(msg) def execute(argv: list[str]) -> None: @@ -459,9 +459,11 @@ def execute(argv: list[str]) -> None: if maybe_path_to_config: # kinda silly but this makes mypy happy path_to_config = maybe_path_to_config else: - raise NRError("could not find previous generation") + raise NixOSRebuildError("could not find previous generation") case (_, True, _, _): - raise NRError(f"--rollback is incompatible with '{action}'") + raise NixOSRebuildError( + f"--rollback is incompatible with '{action}'" + ) case (_, False, Remote(_), Flake(_)): path_to_config = nix.build_remote_flake( attr, diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py index f8439461b6bd..c0183e1a265b 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py @@ -11,7 +11,7 @@ from .process import Remote, run_wrapper type ImageVariants = list[str] -class NRError(Exception): +class NixOSRebuildError(Exception): "nixos-rebuild general error." def __init__(self, message: str) -> None: 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 a55bfd508e37..77113a093a77 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,7 +20,7 @@ from .models import ( Generation, GenerationJson, ImageVariants, - NRError, + NixOSRebuildError, Profile, Remote, ) @@ -256,7 +256,7 @@ def edit(flake: Flake | None, flake_flags: Args | None = None) -> None: ) else: if flake_flags: - raise NRError("'edit' does not support extra Nix flags") + raise NixOSRebuildError("'edit' does not support extra Nix flags") nixos_config = Path( os.getenv("NIXOS_CONFIG") or find_file("nixos-config") or "/etc/nixos" ) @@ -266,7 +266,7 @@ def edit(flake: Flake | None, flake_flags: Args | None = None) -> None: if nixos_config.exists(): run_wrapper([os.getenv("EDITOR", "nano"), nixos_config], check=False) else: - raise NRError("cannot find NixOS config file") + raise NixOSRebuildError("cannot find NixOS config file") def find_file(file: str, nix_flags: Args | None = None) -> Path | None: @@ -424,7 +424,7 @@ def get_generations(profile: Profile) -> list[Generation]: and if this is the current active profile or not. """ if not profile.path.exists(): - raise NRError(f"no profile '{profile.name}' found") + raise NixOSRebuildError(f"no profile '{profile.name}' found") def parse_path(path: Path, profile: Profile) -> Generation: entry_id = path.name.split("-")[1] @@ -456,7 +456,7 @@ def get_generations_from_nix_env( and if this is the current active profile or not. """ if not profile.path.exists(): - raise NRError(f"no profile '{profile.name}' found") + raise NixOSRebuildError(f"no profile '{profile.name}' found") # Using `nix-env --list-generations` needs root to lock the profile r = run_wrapper( @@ -635,13 +635,13 @@ def switch_to_configuration( """ if specialisation: if action not in (Action.SWITCH, Action.TEST): - raise NRError( + raise NixOSRebuildError( "'--specialisation' can only be used with 'switch' and 'test'" ) path_to_config = path_to_config / f"specialisation/{specialisation}" if not path_to_config.exists(): - raise NRError(f"specialisation not found: {specialisation}") + raise NixOSRebuildError(f"specialisation not found: {specialisation}") r = run_wrapper( ["test", "-d", "/run/systemd/system"], 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 d87b6b62dca8..d965212a8d74 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 @@ -714,7 +714,7 @@ def test_switch_to_configuration_without_systemd_run( remote=None, ) - with pytest.raises(m.NRError) as e: + with pytest.raises(m.NixOSRebuildError) as e: n.switch_to_configuration( config_path, m.Action.BOOT,