nixos-rebuild-ng: add --no-reexec flag, deprecate --fast (#377957)

This commit is contained in:
Thiago Kenji Okada
2025-01-31 13:53:50 +00:00
committed by GitHub
5 changed files with 47 additions and 39 deletions
@@ -17,11 +17,12 @@ nixos-rebuild - reconfigure a NixOS machine
# SYNOPSIS
; document here only non-deprecated flags
_nixos-rebuild_ \[--verbose] [--max-jobs MAX_JOBS] [--cores CORES] [--log-format LOG_FORMAT] [--keep-going] [--keep-failed] [--fallback] [--repair] [--option OPTION OPTION] [--builders BUILDERS]++
\[--include INCLUDE] [--quiet] [--print-build-logs] [--show-trace] [--accept-flake-config] [--refresh] [--impure] [--offline] [--no-net] [--recreate-lock-file]++
\[--no-update-lock-file] [--no-write-lock-file] [--no-registries] [--commit-lock-file] [--update-input UPDATE_INPUT] [--override-input OVERRIDE_INPUT OVERRIDE_INPUT]++
\[--no-build-output] [--use-substitutes] [--help] [--file FILE] [--attr ATTR] [--flake [FLAKE]] [--no-flake] [--install-bootloader] [--profile-name PROFILE_NAME]++
\[--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--fast]++
\[--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--no-reexec]++
\[--image-variant VARIANT]++
\[--build-host BUILD_HOST] [--target-host TARGET_HOST]++
\[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-image,build-vm,build-vm-with-bootloader,list-generations}]
@@ -170,7 +171,7 @@ It must be one of the following:
Causes the boot loader to be (re)installed on the device specified by
the relevant configuration options.
*--fast*
*--no-reexec*
Normally, *nixos-rebuild* first finds and builds itself from the
_config.system.build.nixos-rebuild_ attribute from the current user
channel or flake and exec into it. This allows *nixos-rebuild* to run
@@ -18,7 +18,7 @@ logger.setLevel(logging.INFO)
def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentParser]]:
common_flags = argparse.ArgumentParser(add_help=False)
common_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
common_flags.add_argument(
"--verbose",
"-v",
@@ -37,13 +37,13 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
common_flags.add_argument("--repair", action="store_true")
common_flags.add_argument("--option", nargs=2, action="append")
common_build_flags = argparse.ArgumentParser(add_help=False)
common_build_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
common_build_flags.add_argument("--builders")
common_build_flags.add_argument("--include", "-I", action="append")
common_build_flags.add_argument("--print-build-logs", "-L", action="store_true")
common_build_flags.add_argument("--show-trace", action="store_true")
flake_common_flags = argparse.ArgumentParser(add_help=False)
flake_common_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
flake_common_flags.add_argument("--accept-flake-config", action="store_true")
flake_common_flags.add_argument("--refresh", action="store_true")
flake_common_flags.add_argument("--impure", action="store_true")
@@ -57,10 +57,10 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
flake_common_flags.add_argument("--update-input", action="append")
flake_common_flags.add_argument("--override-input", nargs=2, action="append")
classic_build_flags = argparse.ArgumentParser(add_help=False)
classic_build_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
classic_build_flags.add_argument("--no-build-output", "-Q", action="store_true")
copy_flags = argparse.ArgumentParser(add_help=False)
copy_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
copy_flags.add_argument(
"--use-substitutes",
"--substitute-on-destination",
@@ -166,10 +166,15 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
help="Deprecated, use '--sudo' instead",
)
main_parser.add_argument("--no-ssh-tty", action="store_true", help="Deprecated")
main_parser.add_argument(
"--no-reexec",
action="store_true",
help="Do not update nixos-rebuild in-place (also known as re-exec) before build",
)
main_parser.add_argument(
"--fast",
action="store_true",
help="Skip possibly expensive operations",
help="Deprecated, use '--no-reexec' instead",
)
main_parser.add_argument("--build-host", help="Specifies host to perform the build")
main_parser.add_argument(
@@ -223,23 +228,23 @@ def parse_args(
if args.ask_sudo_password:
args.sudo = True
# TODO: use deprecated=True in Python >=3.13
if args.install_grub:
parser_warn("--install-grub deprecated, use --install-bootloader instead")
parser_warn("--install-grub is deprecated, use --install-bootloader instead")
args.install_bootloader = True
# TODO: use deprecated=True in Python >=3.13
if args.use_remote_sudo:
parser_warn("--use-remote-sudo deprecated, use --sudo instead")
parser_warn("--use-remote-sudo is deprecated, use --sudo instead")
args.sudo = True
# TODO: use deprecated=True in Python >=3.13
if args.fast:
parser_warn("--fast is deprecated, use --no-reexec instead")
args.no_reexec = True
if args.no_ssh_tty:
parser_warn("--no-ssh-tty deprecated, SSH's TTY is never used anymore")
parser_warn("--no-ssh-tty is deprecated, SSH's TTY is never used anymore")
# TODO: use deprecated=True in Python >=3.13
if args.no_build_nix:
parser_warn("--no-build-nix deprecated, we do not build nix anymore")
parser_warn("--no-build-nix is deprecated, we do not build nix anymore")
if args.action == Action.EDIT.value and (args.file or args.attr):
parser.error("--file and --attr are not supported with 'edit'")
@@ -351,7 +356,7 @@ def execute(argv: list[str]) -> None:
if (
WITH_REEXEC
and can_run
and not args.fast
and not args.no_reexec
and not os.environ.get("_NIXOS_REBUILD_REEXEC")
):
reexec(argv, args, build_flags, flake_build_flags)
@@ -144,7 +144,7 @@ def test_execute_nix_boot(mock_run: Any, tmp_path: Path) -> None:
mock_run.side_effect = run_side_effect
nr.execute(["nixos-rebuild", "boot", "--no-flake", "-vvv", "--fast"])
nr.execute(["nixos-rebuild", "boot", "--no-flake", "-vvv", "--no-reexec"])
assert mock_run.call_count == 6
mock_run.assert_has_calls(
@@ -222,7 +222,7 @@ def test_execute_nix_build_vm(mock_run: Any, tmp_path: Path) -> None:
"nixos-config=./configuration.nix",
"-I",
"nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs",
"--fast",
"--no-reexec",
]
)
@@ -340,7 +340,7 @@ def test_execute_nix_switch_flake(mock_run: Any, tmp_path: Path) -> None:
"--install-bootloader",
"--sudo",
"--verbose",
"--fast",
"--no-reexec",
# https://github.com/NixOS/nixpkgs/issues/374050
"--option",
"narinfo-cache-negative-ttl",
@@ -418,7 +418,7 @@ def test_execute_nix_switch_flake_target_host(
"--use-remote-sudo",
"--target-host",
"user@localhost",
"--fast",
"--no-reexec",
]
)
@@ -508,7 +508,7 @@ def test_execute_nix_switch_flake_build_host(
"/path/to/config#hostname",
"--build-host",
"user@localhost",
"--fast",
"--no-reexec",
]
)
@@ -587,7 +587,7 @@ def test_execute_switch_rollback(mock_run: Any, tmp_path: Path) -> None:
nixpkgs_path.touch()
nr.execute(
["nixos-rebuild", "switch", "--rollback", "--install-bootloader", "--fast"]
["nixos-rebuild", "switch", "--rollback", "--install-bootloader", "--no-reexec"]
)
assert mock_run.call_count >= 2
@@ -625,7 +625,7 @@ def test_execute_build(mock_run: Any, tmp_path: Path) -> None:
CompletedProcess([], 0, str(config_path)),
]
nr.execute(["nixos-rebuild", "build", "--no-flake", "--fast"])
nr.execute(["nixos-rebuild", "build", "--no-flake", "--no-reexec"])
assert mock_run.call_count == 1
mock_run.assert_has_calls(
@@ -659,7 +659,7 @@ def test_execute_test_flake(mock_run: Any, tmp_path: Path) -> None:
mock_run.side_effect = run_side_effect
nr.execute(
["nixos-rebuild", "test", "--flake", "github:user/repo#hostname", "--fast"]
["nixos-rebuild", "test", "--flake", "github:user/repo#hostname", "--no-reexec"]
)
assert mock_run.call_count == 2
@@ -712,7 +712,7 @@ def test_execute_test_rollback(
mock_run.side_effect = run_side_effect
nr.execute(
["nixos-rebuild", "test", "--rollback", "--profile-name", "foo", "--fast"]
["nixos-rebuild", "test", "--rollback", "--profile-name", "foo", "--no-reexec"]
)
assert mock_run.call_count == 2
@@ -20,7 +20,7 @@ from .helpers import get_qualified_name
autospec=True,
return_value=CompletedProcess([], 0, stdout=" \n/path/to/file\n "),
)
def test_build(mock_run: Any, monkeypatch: Any) -> None:
def test_build(mock_run: Any) -> None:
assert n.build(
"config.system.build.attr",
m.BuildAttr("<nixpkgs/nixos>", None),
@@ -79,7 +79,7 @@ def test_build_flake(mock_run: Any, monkeypatch: MonkeyPatch, tmpdir: Path) -> N
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
@patch(get_qualified_name(n.uuid4, n), autospec=True)
def test_build_remote(mock_uuid4: Any, mock_run: Any, monkeypatch: Any) -> None:
def test_build_remote(mock_uuid4: Any, mock_run: Any, monkeypatch: MonkeyPatch) -> None:
build_host = m.Remote("user@host", [], None)
monkeypatch.setenv("NIX_SSHOPTS", "--ssh opts")
@@ -226,7 +226,7 @@ def test_build_remote_flake(
)
def test_copy_closure(monkeypatch: Any) -> None:
def test_copy_closure(monkeypatch: MonkeyPatch) -> None:
closure = Path("/path/to/closure")
with patch(get_qualified_name(n.run_wrapper, n), autospec=True) as mock_run:
n.copy_closure(closure, None)
@@ -290,7 +290,7 @@ def test_copy_closure(monkeypatch: Any) -> None:
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
def test_edit(mock_run: Any, monkeypatch: MonkeyPatch, tmpdir: Path) -> None:
# Flake
flake = m.Flake.parse(f"{tmpdir}#attr")
n.edit(flake, {"commit_lock_file": True})
@@ -309,8 +309,8 @@ def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
# Classic
with monkeypatch.context() as mp:
default_nix = tmpdir.join("default.nix")
default_nix.write("{}")
default_nix = tmpdir / "default.nix"
default_nix.write_text("{}", encoding="utf-8")
mp.setenv("NIXOS_CONFIG", str(tmpdir))
mp.setenv("EDITOR", "editor")
@@ -333,7 +333,7 @@ def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
""",
),
)
def test_get_build_image_variants(mock_run: Any) -> None:
def test_get_build_image_variants(mock_run: Any, tmp_path: Path) -> None:
build_attr = m.BuildAttr("<nixpkgs/nixos>", None)
assert n.get_build_image_variants(build_attr) == {
"azure": "nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd",
@@ -357,7 +357,7 @@ def test_get_build_image_variants(mock_run: Any) -> None:
stdout=PIPE,
)
build_attr = m.BuildAttr(Path("/tmp"), "preAttr")
build_attr = m.BuildAttr(Path(tmp_path), "preAttr")
assert n.get_build_image_variants(build_attr, {"inst_flag": True}) == {
"azure": "nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd",
"vmware": "nixos-image-vmware-25.05.20250102.6df2492-x86_64-linux.vmdk",
@@ -369,10 +369,10 @@ def test_get_build_image_variants(mock_run: Any) -> None:
"--strict",
"--json",
"--expr",
textwrap.dedent("""
textwrap.dedent(f"""
let
value = import "/tmp";
set = if builtins.isFunction value then value {} else value;
value = import "{tmp_path}";
set = if builtins.isFunction value then value {{}} else value;
in
builtins.mapAttrs (n: v: v.passthru.filePath) set.preAttr.config.system.build.images
"""),
@@ -687,7 +687,7 @@ def test_set_profile(mock_run: Any) -> None:
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_switch_to_configuration(mock_run: Any, monkeypatch: Any) -> None:
def test_switch_to_configuration(mock_run: Any, monkeypatch: MonkeyPatch) -> None:
profile_path = Path("/path/to/profile")
config_path = Path("/path/to/config")
@@ -1,6 +1,8 @@
from typing import Any
from unittest.mock import patch
from pytest import MonkeyPatch
import nixos_rebuild.models as m
import nixos_rebuild.process as p
@@ -94,7 +96,7 @@ def test_run(mock_run: Any) -> None:
)
def test_remote_from_name(monkeypatch: Any) -> None:
def test_remote_from_name(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setenv("NIX_SSHOPTS", "")
assert m.Remote.from_arg("user@localhost", None, False) == m.Remote(
"user@localhost",