staging-nixos merge for 2026-03-08 (#497850)
This commit is contained in:
@@ -23969,6 +23969,12 @@
|
||||
githubId = 15986681;
|
||||
name = "Simon Bruder";
|
||||
};
|
||||
scandiravian = {
|
||||
email = "nixos@scandiravian.com";
|
||||
github = "scandiravian";
|
||||
githubId = 13556969;
|
||||
name = "Scandiravian";
|
||||
};
|
||||
scd31 = {
|
||||
name = "scd31";
|
||||
github = "scd31";
|
||||
|
||||
@@ -249,6 +249,8 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.
|
||||
`component.settings`. The unix module now supports using SSH keys from Kanidm via
|
||||
`services.kanidm.unix.sshIntegration = true`.
|
||||
|
||||
- `mdbook-linkcheck` has been removed as it is unmaintained and incompatible with the latest version of `mdbook`. Users can instead migrate to `mdbook-linkcheck2`.
|
||||
|
||||
- `glibc` has been updated to version 2.42.
|
||||
|
||||
This version no longer makes the stack executable when a shared library requires this. A symptom
|
||||
|
||||
@@ -1130,6 +1130,9 @@ in
|
||||
nixos-rebuild-target-host = runTest {
|
||||
imports = [ ./nixos-rebuild-target-host.nix ];
|
||||
};
|
||||
nixos-rebuild-target-host-interrupted = runTest {
|
||||
imports = [ ./nixos-rebuild-target-host-interrupted.nix ];
|
||||
};
|
||||
nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
|
||||
nixpkgs-config-allow-unfree =
|
||||
pkgs.callPackage ../modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
{ hostPkgs, ... }:
|
||||
|
||||
# This test recreates a remote deployment scenario where the connection
|
||||
# between deployer and target is closed during the deployment - in this
|
||||
# case because the connection goes over a 'reverse ssh' tunnel service
|
||||
# that has changes that are being deployed.
|
||||
|
||||
# This is not seamless (the deployer doesn't get to see the logs after
|
||||
# the disconnect), but is a lot better than the old behaviour, where
|
||||
# the switch was aborted and the connection never restored.
|
||||
|
||||
{
|
||||
name = "nixos-rebuild-target-host-interrupted";
|
||||
|
||||
# TODO: remove overlay from nixos/modules/profiles/installation-device.nix
|
||||
# make it a _small package instead, then remove pkgsReadOnly = false;.
|
||||
node.pkgsReadOnly = false;
|
||||
|
||||
nodes = {
|
||||
deployer =
|
||||
{
|
||||
nodes,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../modules/profiles/installation-device.nix
|
||||
];
|
||||
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = 1;
|
||||
};
|
||||
|
||||
system.includeBuildDependencies = true;
|
||||
|
||||
virtualisation = {
|
||||
cores = 2;
|
||||
memorySize = 3072;
|
||||
};
|
||||
|
||||
services.openssh.enable = true;
|
||||
users.users.root.openssh.authorizedKeys.keys = [ nodes.target.system.build.publicKey ];
|
||||
system.extraDependencies = [
|
||||
# so that it doesn't need to be built inside the test
|
||||
pkgs.nixVersions.latest
|
||||
];
|
||||
|
||||
system.build.privateKey = snakeOilPrivateKey;
|
||||
system.build.publicKey = snakeOilPublicKey;
|
||||
system.switch.enable = true;
|
||||
|
||||
services.getty.autologinUser = lib.mkForce "root";
|
||||
};
|
||||
|
||||
target =
|
||||
{
|
||||
nodes,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
|
||||
targetConfig = {
|
||||
documentation.enable = false;
|
||||
services.openssh.enable = true;
|
||||
system.build.privateKey = snakeOilPrivateKey;
|
||||
system.build.publicKey = snakeOilPublicKey;
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ];
|
||||
users.users.alice.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ];
|
||||
users.users.bob.openssh.authorizedKeys.keys = [ nodes.deployer.system.build.publicKey ];
|
||||
|
||||
users.users.alice.extraGroups = [ "wheel" ];
|
||||
users.users.bob.extraGroups = [ "wheel" ];
|
||||
|
||||
# Disable sudo for root to ensure sudo isn't called without `--sudo`
|
||||
security.sudo.extraRules = lib.mkForce [
|
||||
{
|
||||
groups = [ "wheel" ];
|
||||
commands = [ { command = "ALL"; } ];
|
||||
}
|
||||
{
|
||||
users = [ "alice" ];
|
||||
commands = [
|
||||
{
|
||||
command = "ALL";
|
||||
options = [ "NOPASSWD" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
nix.settings.trusted-users = [ "@wheel" ];
|
||||
|
||||
systemd.services."autossh-ng" = {
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
User = "root";
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
ExecStart = "${pkgs.openssh}/bin/ssh -o \"ServerAliveInterval 30\" -o \"ServerAliveCountMax 3\" -o ExitOnForwardFailure=yes -N -R2222:localhost:22 deployer";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [ ./common/user-account.nix ];
|
||||
|
||||
config = lib.mkMerge [
|
||||
targetConfig
|
||||
{
|
||||
system.build = {
|
||||
inherit targetConfig;
|
||||
};
|
||||
system.switch.enable = true;
|
||||
|
||||
networking.hostName = "target";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
let
|
||||
sshConfig = builtins.toFile "ssh.conf" ''
|
||||
UserKnownHostsFile=/dev/null
|
||||
StrictHostKeyChecking=no
|
||||
'';
|
||||
|
||||
targetConfigJSON = hostPkgs.writeText "target-configuration.json" (
|
||||
builtins.toJSON nodes.target.system.build.targetConfig
|
||||
);
|
||||
|
||||
targetNetworkJSON = hostPkgs.writeText "target-network.json" (
|
||||
builtins.toJSON nodes.target.system.build.networkConfig
|
||||
);
|
||||
|
||||
configFile =
|
||||
hostname:
|
||||
hostPkgs.writeText "configuration.nix" # nix
|
||||
''
|
||||
{ lib, pkgs, modulesPath, ... }: {
|
||||
imports = [
|
||||
(modulesPath + "/virtualisation/qemu-vm.nix")
|
||||
(modulesPath + "/testing/test-instrumentation.nix")
|
||||
(modulesPath + "/../tests/common/user-account.nix")
|
||||
(lib.modules.importJSON ./target-configuration.json)
|
||||
(lib.modules.importJSON ./target-network.json)
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
|
||||
boot.loader.grub = {
|
||||
enable = true;
|
||||
device = "/dev/vda";
|
||||
forceInstall = true;
|
||||
};
|
||||
|
||||
# needed to make NIX_SSHOPTS work for nix-copy-closure
|
||||
nix.package = pkgs.nixVersions.latest;
|
||||
|
||||
# We're changing the '-E' parameter to the new hostname here,
|
||||
# not because we care about the logs, but because we want to
|
||||
# force the scenario where the connection is broken during the
|
||||
# deployment (because the autossh-ng service is stopped and
|
||||
# started):
|
||||
systemd.services."autossh-ng".serviceConfig.ExecStart =
|
||||
lib.mkForce "''${pkgs.openssh}/bin/ssh -o \"ServerAliveInterval 30\" -o \"ServerAliveCountMax 3\" -o ExitOnForwardFailure=yes -N -R2222:localhost:22 -E ${hostname} deployer";
|
||||
|
||||
# this will be asserted to validate the switch happened:
|
||||
networking.hostName = "${hostname}";
|
||||
}
|
||||
'';
|
||||
in
|
||||
# python
|
||||
''
|
||||
start_all()
|
||||
target.wait_for_open_port(22)
|
||||
|
||||
deployer.wait_until_succeeds("ping -c1 target")
|
||||
deployer.succeed("install -Dm 600 ${nodes.deployer.system.build.privateKey} ~root/.ssh/id_ecdsa")
|
||||
deployer.succeed("install ${sshConfig} ~root/.ssh/config")
|
||||
|
||||
target.succeed("nixos-generate-config")
|
||||
target.succeed("install -Dm 600 ${nodes.target.system.build.privateKey} ~root/.ssh/id_ecdsa")
|
||||
target.succeed("install ${sshConfig} ~root/.ssh/config")
|
||||
deployer.succeed("scp alice@target:/etc/nixos/hardware-configuration.nix /root/hardware-configuration.nix")
|
||||
target.wait_for_unit("autossh-ng.service")
|
||||
|
||||
deployer.copy_from_host("${configFile "config-1-deployed"}", "/root/configuration-1.nix")
|
||||
deployer.copy_from_host("${configFile "config-2-deployed"}", "/root/configuration-2.nix")
|
||||
deployer.copy_from_host("${targetNetworkJSON}", "/root/target-network.json")
|
||||
deployer.copy_from_host("${targetConfigJSON}", "/root/target-configuration.json")
|
||||
|
||||
with subtest("Deploy to alice@target via reverse ssh"):
|
||||
deployer.wait_for_unit("multi-user.target")
|
||||
# Uses TTY/send_chars instead of deployer.succeed to set NIX_SSHOPTS
|
||||
deployer.send_chars("NIX_SSHOPTS=\"-p 2222\" nixos-rebuild switch -I nixos-config=/root/configuration-1.nix --target-host alice@localhost --sudo\n")
|
||||
|
||||
# the connection breaks, but the 'switch' should now continue in the background:
|
||||
deployer.wait_until_tty_matches("1", "error: while running command with remote sudo")
|
||||
|
||||
def deployed(last_try: bool) -> bool:
|
||||
target_hostname = deployer.succeed("ssh alice@target cat /etc/hostname", timeout=20).rstrip()
|
||||
if last_try:
|
||||
print(f"Still seeing hostname {target_hostname}")
|
||||
return target_hostname == "config-1-deployed"
|
||||
retry(deployed)
|
||||
|
||||
with subtest("Deploy to bob@target via reverse ssh with password-based sudo"):
|
||||
deployer.wait_for_unit("multi-user.target")
|
||||
# Uses TTY/send_chars instead of deployer.succeed to set NIX_SSHOPTS and for ask-sudo-password
|
||||
deployer.send_chars("NIX_SSHOPTS=\"-p 2222\" nixos-rebuild switch -I nixos-config=/root/configuration-2.nix --target-host bob@localhost --ask-sudo-password\n")
|
||||
deployer.wait_until_tty_matches("1", "password for bob")
|
||||
deployer.send_chars("${nodes.target.users.users.bob.password}\n")
|
||||
|
||||
# the connection breaks, but the 'switch' should now continue in the background:
|
||||
deployer.wait_until_tty_matches("1", "error: while running command with remote sudo")
|
||||
|
||||
def deployed(last_try: bool) -> bool:
|
||||
target_hostname = deployer.succeed("ssh alice@target cat /etc/hostname", timeout=20).rstrip()
|
||||
if last_try:
|
||||
print(f"Still seeing hostname {target_hostname}")
|
||||
return target_hostname == "config-2-deployed"
|
||||
retry(deployed)
|
||||
'';
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
rustPlatform,
|
||||
pkg-config,
|
||||
openssl,
|
||||
testers,
|
||||
mdbook-linkcheck,
|
||||
}:
|
||||
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "mdbook-linkcheck";
|
||||
version = "0.7.7";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Michael-F-Bryan";
|
||||
repo = "mdbook-linkcheck";
|
||||
rev = "v${finalAttrs.version}";
|
||||
sha256 = "sha256-ZbraChBHuKAcUA62EVHZ1RygIotNEEGv24nhSPAEj00=";
|
||||
};
|
||||
|
||||
cargoHash = "sha256-Tt7ljjWv2CMtP/ELZNgSH/ifmBk/42+E0r9ZXQEJNP8=";
|
||||
|
||||
buildInputs = lib.optionals (!stdenv.hostPlatform.isDarwin) [ openssl ];
|
||||
|
||||
nativeBuildInputs = lib.optionals (!stdenv.hostPlatform.isDarwin) [ pkg-config ];
|
||||
|
||||
env.OPENSSL_NO_VENDOR = 1;
|
||||
|
||||
doCheck = false; # tries to access network to test broken web link functionality
|
||||
|
||||
passthru.tests.version = testers.testVersion { package = mdbook-linkcheck; };
|
||||
|
||||
meta = {
|
||||
description = "Backend for `mdbook` which will check your links for you";
|
||||
mainProgram = "mdbook-linkcheck";
|
||||
homepage = "https://github.com/Michael-F-Bryan/mdbook-linkcheck";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = with lib.maintainers; [
|
||||
zhaofengli
|
||||
matthiasbeyer
|
||||
];
|
||||
};
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
lib,
|
||||
fetchFromGitHub,
|
||||
rustPlatform,
|
||||
testers,
|
||||
mdbook-linkcheck2,
|
||||
}:
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "mdbook-linkcheck2";
|
||||
version = "0.12.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "marxin";
|
||||
repo = "mdbook-linkcheck2";
|
||||
tag = "v${finalAttrs.version}";
|
||||
sha256 = "sha256-SvheBEIWiL1zdYeMQalbBeAQC86DycqV1/PTA+0S7Gg=";
|
||||
};
|
||||
|
||||
cargoHash = "sha256-s4nvVHl/bViIxZfqc4SxSnCCYIY/hxy0C7f2/9ztqts=";
|
||||
|
||||
doCheck = false; # tries to access network to test broken web link functionality
|
||||
|
||||
passthru.tests.version = testers.testVersion { package = mdbook-linkcheck2; };
|
||||
|
||||
meta = {
|
||||
description = "Backend for mdbook which will check your links for you";
|
||||
mainProgram = "mdbook-linkcheck2";
|
||||
homepage = "https://github.com/marxin/mdbook-linkcheck2";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = with lib.maintainers; [
|
||||
scandiravian
|
||||
];
|
||||
};
|
||||
})
|
||||
@@ -25,12 +25,14 @@ from .models import (
|
||||
Profile,
|
||||
Remote,
|
||||
)
|
||||
from .process import PRESERVE_ENV, SSH_DEFAULT_OPTS, run_wrapper
|
||||
from .process import PRESERVE_ENV, SSH_DEFAULT_OPTS, run_wrapper, run_wrapper_bg
|
||||
from .process import Args as ProcessArgs
|
||||
from .utils import Args, dict_to_flags
|
||||
|
||||
FLAKE_FLAGS: Final = ["--extra-experimental-features", "nix-command flakes"]
|
||||
FLAKE_REPL_TEMPLATE: Final = "repl.nix.template"
|
||||
SWITCH_TO_CONFIGURATION_CMD_PREFIX: Final = [
|
||||
SYSTEMD_RUN_UNIT_PREFIX: Final = "nixos-rebuild-switch-to-configuration"
|
||||
SYSTEMD_RUN_CMD_PREFIX: Final = [
|
||||
"systemd-run",
|
||||
"-E",
|
||||
# Will be set to new value early in switch-to-configuration script,
|
||||
@@ -41,11 +43,10 @@ SWITCH_TO_CONFIGURATION_CMD_PREFIX: Final = [
|
||||
"-E",
|
||||
"NIXOS_NO_CHECK",
|
||||
"--collect",
|
||||
"--wait",
|
||||
"--no-ask-password",
|
||||
"--pipe",
|
||||
"--quiet",
|
||||
"--service-type=exec",
|
||||
"--unit=nixos-rebuild-switch-to-configuration",
|
||||
]
|
||||
logger: Final = logging.getLogger(__name__)
|
||||
|
||||
@@ -669,6 +670,78 @@ def set_profile(
|
||||
)
|
||||
|
||||
|
||||
def _has_systemd(target_host: Remote | None) -> bool:
|
||||
r = run_wrapper(
|
||||
["test", "-d", "/run/systemd/system"],
|
||||
remote=target_host,
|
||||
check=False,
|
||||
)
|
||||
return r.returncode == 0
|
||||
|
||||
|
||||
def _run_action(
|
||||
action: Literal[Action.SWITCH, Action.BOOT, Action.TEST, Action.DRY_ACTIVATE],
|
||||
path_to_config: Path,
|
||||
install_bootloader: bool,
|
||||
target_host: Remote | None,
|
||||
sudo: bool,
|
||||
prefix: ProcessArgs | None = None,
|
||||
) -> None:
|
||||
cmd: ProcessArgs = [path_to_config / "bin/switch-to-configuration", str(action)]
|
||||
if prefix:
|
||||
cmd = [*prefix, *cmd]
|
||||
|
||||
run_wrapper(
|
||||
cmd,
|
||||
env={
|
||||
"LOCALE_ARCHIVE": PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1" if install_bootloader else "0",
|
||||
},
|
||||
remote=target_host,
|
||||
sudo=sudo,
|
||||
)
|
||||
|
||||
|
||||
def _run_action_with_systemd(
|
||||
action: Literal[Action.SWITCH, Action.BOOT, Action.TEST, Action.DRY_ACTIVATE],
|
||||
path_to_config: Path,
|
||||
install_bootloader: bool,
|
||||
target_host: Remote | None,
|
||||
sudo: bool,
|
||||
) -> None:
|
||||
unique_unit_name = SYSTEMD_RUN_UNIT_PREFIX + "-" + uuid.uuid4().hex[:8]
|
||||
journalctl = run_wrapper_bg(
|
||||
[
|
||||
"journalctl",
|
||||
"-f",
|
||||
f"--unit={unique_unit_name}",
|
||||
"--output=cat",
|
||||
],
|
||||
remote=target_host,
|
||||
sudo=sudo,
|
||||
)
|
||||
|
||||
try:
|
||||
_run_action(
|
||||
action,
|
||||
path_to_config,
|
||||
install_bootloader,
|
||||
target_host,
|
||||
sudo,
|
||||
prefix=[*SYSTEMD_RUN_CMD_PREFIX, f"--unit={unique_unit_name}"],
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
run_wrapper(
|
||||
["systemctl", "stop", unique_unit_name],
|
||||
remote=target_host,
|
||||
sudo=sudo,
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
journalctl.terminate()
|
||||
|
||||
|
||||
def switch_to_configuration(
|
||||
path_to_config: Path,
|
||||
action: Literal[Action.SWITCH, Action.BOOT, Action.TEST, Action.DRY_ACTIVATE],
|
||||
@@ -692,29 +765,26 @@ def switch_to_configuration(
|
||||
if not path_to_config.exists():
|
||||
raise NixOSRebuildError(f"specialisation not found: {specialisation}")
|
||||
|
||||
r = run_wrapper(
|
||||
["test", "-d", "/run/systemd/system"],
|
||||
remote=target_host,
|
||||
check=False,
|
||||
)
|
||||
cmd = SWITCH_TO_CONFIGURATION_CMD_PREFIX
|
||||
if r.returncode:
|
||||
if _has_systemd(target_host):
|
||||
_run_action_with_systemd(
|
||||
action,
|
||||
path_to_config,
|
||||
install_bootloader,
|
||||
target_host,
|
||||
sudo,
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"skipping systemd-run to switch configuration since systemd is "
|
||||
"not working in target host"
|
||||
)
|
||||
cmd = []
|
||||
|
||||
run_wrapper(
|
||||
[*cmd, path_to_config / "bin/switch-to-configuration", str(action)],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1" if install_bootloader else "0",
|
||||
},
|
||||
remote=target_host,
|
||||
sudo=sudo,
|
||||
)
|
||||
_run_action(
|
||||
action,
|
||||
path_to_config,
|
||||
install_bootloader,
|
||||
target_host,
|
||||
sudo,
|
||||
)
|
||||
|
||||
|
||||
def upgrade_channels(all_channels: bool = False, sudo: bool = False) -> None:
|
||||
|
||||
@@ -9,7 +9,7 @@ from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from ipaddress import AddressValueError, IPv6Address
|
||||
from typing import Final, Literal, Self, TextIO, TypedDict, Unpack, override
|
||||
from typing import Final, Literal, NamedTuple, Self, TextIO, TypedDict, Unpack, override
|
||||
|
||||
from . import tmpdir
|
||||
|
||||
@@ -117,16 +117,18 @@ def cleanup_ssh() -> None:
|
||||
atexit.register(cleanup_ssh)
|
||||
|
||||
|
||||
def run_wrapper(
|
||||
class _RunParams(NamedTuple):
|
||||
args: Args
|
||||
popen_env: dict[str, str] | None
|
||||
process_input: str | None
|
||||
|
||||
|
||||
def _build_run_params(
|
||||
args: Args,
|
||||
*,
|
||||
check: bool = True,
|
||||
env: Mapping[str, EnvValue] | None = None,
|
||||
remote: Remote | None = None,
|
||||
sudo: bool = False,
|
||||
**kwargs: Unpack[RunKwargs],
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
"Wrapper around `subprocess.run` that supports extra functionality."
|
||||
env: Mapping[str, EnvValue] | None,
|
||||
remote: Remote | None,
|
||||
sudo: bool,
|
||||
) -> _RunParams:
|
||||
process_input = None
|
||||
run_args: list[Arg] = list(args)
|
||||
final_args: list[Arg]
|
||||
@@ -188,9 +190,63 @@ def run_wrapper(
|
||||
final_args = run_args
|
||||
popen_env = None if env is None else resolved_env
|
||||
|
||||
return _RunParams(final_args, popen_env, process_input)
|
||||
|
||||
|
||||
def run_wrapper_bg(
|
||||
args: Args,
|
||||
env: Mapping[str, EnvValue] | None = None,
|
||||
remote: Remote | None = None,
|
||||
sudo: bool = False,
|
||||
) -> subprocess.Popen[str]:
|
||||
"Wrapper around `subprocess.Popen` that supports extra functionality."
|
||||
(final_args, popen_env, process_input) = _build_run_params(args, env, remote, sudo)
|
||||
|
||||
logger.debug(
|
||||
"calling Popen with args=%r",
|
||||
_sanitize_env_run_args(list(final_args)),
|
||||
)
|
||||
|
||||
if process_input:
|
||||
stdin = subprocess.PIPE
|
||||
else:
|
||||
stdin = None
|
||||
|
||||
r = subprocess.Popen(
|
||||
final_args,
|
||||
env=popen_env,
|
||||
stdin=stdin,
|
||||
# Hope nobody is using NixOS with non-UTF8 encodings, but
|
||||
# "surrogateescape" should still work in those systems.
|
||||
text=True,
|
||||
errors="surrogateescape",
|
||||
)
|
||||
|
||||
if r.stdin and process_input:
|
||||
r.stdin.write(process_input)
|
||||
r.stdin.write("\n")
|
||||
r.stdin.close()
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def run_wrapper(
|
||||
args: Args,
|
||||
*,
|
||||
check: bool = True,
|
||||
env: Mapping[str, EnvValue] | None = None,
|
||||
remote: Remote | None = None,
|
||||
sudo: bool = False,
|
||||
**kwargs: Unpack[RunKwargs],
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
"Wrapper around `subprocess.run` that supports extra functionality."
|
||||
(final_args, popen_env, process_input) = _build_run_params(
|
||||
list(args), env, remote, sudo
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
"calling run with args=%r, kwargs=%r, env=%r",
|
||||
_sanitize_env_run_args(remote_run_args if remote else run_args),
|
||||
_sanitize_env_run_args(list(final_args)),
|
||||
kwargs,
|
||||
env,
|
||||
)
|
||||
|
||||
@@ -166,8 +166,15 @@ def test_parse_args() -> None:
|
||||
{"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
|
||||
clear=True,
|
||||
)
|
||||
@patch("uuid.uuid4")
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_nix_boot(
|
||||
mock_popen: Mock, mock_run: Mock, mock_uuid4: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
nixpkgs_path = tmp_path / "nixpkgs"
|
||||
(nixpkgs_path / ".git").mkdir(parents=True)
|
||||
config_path = tmp_path / "test"
|
||||
@@ -238,7 +245,8 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None:
|
||||
),
|
||||
call(
|
||||
[
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
config_path / "bin/switch-to-configuration",
|
||||
"boot",
|
||||
],
|
||||
@@ -259,7 +267,8 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None:
|
||||
# https://github.com/NixOS/nixpkgs/issues/437872
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_nix_build(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_nix_build(mock_popen: Mock, mock_run: Mock, tmp_path: Path) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -301,7 +310,8 @@ def test_execute_nix_build(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_nix_build_vm(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_nix_build_vm(mock_popen: Mock, mock_run: Mock, tmp_path: Path) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -350,7 +360,10 @@ def test_execute_nix_build_vm(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_nix_build_image_flake(
|
||||
mock_popen: Mock, mock_run: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -432,11 +445,18 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
{"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
|
||||
clear=True,
|
||||
)
|
||||
@patch("uuid.uuid4")
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_nix_switch_flake(
|
||||
mock_popen: Mock, mock_run: Mock, mock_uuid4: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
|
||||
if args[0] == "nix":
|
||||
return CompletedProcess([], 0, str(config_path))
|
||||
@@ -506,7 +526,8 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
"env",
|
||||
"-i",
|
||||
"NIXOS_INSTALL_BOOTLOADER=1",
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
config_path / "bin/switch-to-configuration",
|
||||
"switch",
|
||||
],
|
||||
@@ -523,11 +544,13 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
clear=True,
|
||||
)
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
@patch("uuid.uuid4", autospec=True)
|
||||
@patch(get_qualified_name(nr.services.cleanup_ssh), autospec=True)
|
||||
def test_execute_nix_switch_build_target_host(
|
||||
mock_cleanup_ssh: Mock,
|
||||
mock_uuid4: Mock,
|
||||
mock_popen: Mock,
|
||||
mock_run: Mock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
@@ -551,7 +574,8 @@ def test_execute_nix_switch_build_target_host(
|
||||
return CompletedProcess([], 0)
|
||||
|
||||
mock_run.side_effect = run_side_effect
|
||||
mock_uuid4.return_value = uuid.UUID(int=0)
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.side_effect = [uuid.UUID(int=0), uuid.UUID(int=1), test_uuid]
|
||||
|
||||
nr.execute(
|
||||
[
|
||||
@@ -644,7 +668,7 @@ def test_execute_nix_switch_build_target_host(
|
||||
"--realise",
|
||||
str(config_path),
|
||||
"--add-root",
|
||||
"/tmp/tmpdir/00000000000000000000000000000000",
|
||||
"/tmp/tmpdir/00000000000000000000000000000001",
|
||||
],
|
||||
check=True,
|
||||
stdout=PIPE,
|
||||
@@ -748,7 +772,8 @@ def test_execute_nix_switch_build_target_host(
|
||||
"-c",
|
||||
"""'exec env -i PATH="${PATH-}" LOCALE_ARCHIVE="${LOCALE_ARCHIVE-}" NIXOS_NO_CHECK="${NIXOS_NO_CHECK-}" NIXOS_INSTALL_BOOTLOADER=0 "$@"'""",
|
||||
"sh",
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
str(config_path / "bin/switch-to-configuration"),
|
||||
"switch",
|
||||
],
|
||||
@@ -764,13 +789,20 @@ def test_execute_nix_switch_build_target_host(
|
||||
{"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
|
||||
clear=True,
|
||||
)
|
||||
@patch("uuid.uuid4")
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
@patch(get_qualified_name(nr.services.cleanup_ssh), autospec=True)
|
||||
def test_execute_nix_switch_flake_target_host(
|
||||
mock_cleanup_ssh: Mock,
|
||||
mock_popen: Mock,
|
||||
mock_run: Mock,
|
||||
mock_uuid4: Mock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -865,7 +897,8 @@ def test_execute_nix_switch_flake_target_host(
|
||||
"-c",
|
||||
"""'exec env -i PATH="${PATH-}" LOCALE_ARCHIVE="${LOCALE_ARCHIVE-}" NIXOS_NO_CHECK="${NIXOS_NO_CHECK-}" NIXOS_INSTALL_BOOTLOADER=0 "$@"'""",
|
||||
"sh",
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
str(config_path / "bin/switch-to-configuration"),
|
||||
"switch",
|
||||
],
|
||||
@@ -881,13 +914,20 @@ def test_execute_nix_switch_flake_target_host(
|
||||
{"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
|
||||
clear=True,
|
||||
)
|
||||
@patch("uuid.uuid4")
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
@patch(get_qualified_name(nr.services.cleanup_ssh), autospec=True)
|
||||
def test_execute_nix_switch_flake_build_host(
|
||||
mock_cleanup_ssh: Mock,
|
||||
mock_popen: Mock,
|
||||
mock_run: Mock,
|
||||
mock_uuid4: Mock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -984,7 +1024,8 @@ def test_execute_nix_switch_flake_build_host(
|
||||
),
|
||||
call(
|
||||
[
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
config_path / "bin/switch-to-configuration",
|
||||
"switch",
|
||||
],
|
||||
@@ -996,7 +1037,10 @@ def test_execute_nix_switch_flake_build_host(
|
||||
|
||||
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_switch_rollback(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_switch_rollback(
|
||||
mock_popen: Mock, mock_run: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
nixpkgs_path = tmp_path / "nixpkgs"
|
||||
(nixpkgs_path / ".git").mkdir(parents=True)
|
||||
|
||||
@@ -1073,7 +1117,8 @@ def test_execute_switch_rollback(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_build(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_build(mock_popen: Mock, mock_run: Mock, tmp_path: Path) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
mock_run.side_effect = [
|
||||
@@ -1102,8 +1147,9 @@ def test_execute_build(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_build_dry_run_build_and_target_remote(
|
||||
mock_run: Mock, tmp_path: Path
|
||||
mock_popen: Mock, mock_run: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
@@ -1174,7 +1220,8 @@ def test_execute_build_dry_run_build_and_target_remote(
|
||||
|
||||
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_test_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_test_flake(mock_popen: Mock, mock_run: Mock, tmp_path: Path) -> None:
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
|
||||
@@ -1224,11 +1271,13 @@ def test_execute_test_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
@patch("pathlib.Path.exists", autospec=True, return_value=True)
|
||||
@patch("pathlib.Path.mkdir", autospec=True)
|
||||
def test_execute_test_rollback(
|
||||
mock_path_mkdir: Mock,
|
||||
mock_path_exists: Mock,
|
||||
mock_popen: Mock,
|
||||
mock_run: Mock,
|
||||
) -> None:
|
||||
def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
|
||||
@@ -1291,8 +1340,15 @@ def test_execute_test_rollback(
|
||||
{"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
|
||||
clear=True,
|
||||
)
|
||||
@patch("uuid.uuid4", autospec=True)
|
||||
@patch("subprocess.run", autospec=True)
|
||||
def test_execute_switch_store_path(mock_run: Mock, tmp_path: Path) -> None:
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_switch_store_path(
|
||||
mock_popen: Mock, mock_run: Mock, mock_uuid4: Mock, tmp_path: Path
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
config_path = tmp_path / "test-system"
|
||||
config_path.mkdir()
|
||||
|
||||
@@ -1330,7 +1386,8 @@ def test_execute_switch_store_path(mock_run: Mock, tmp_path: Path) -> None:
|
||||
),
|
||||
call(
|
||||
[
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
config_path / "bin/switch-to-configuration",
|
||||
"switch",
|
||||
],
|
||||
@@ -1349,13 +1406,20 @@ def test_execute_switch_store_path(mock_run: Mock, tmp_path: Path) -> None:
|
||||
|
||||
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
@patch("uuid.uuid4")
|
||||
@patch("subprocess.run", autospec=True)
|
||||
@patch(get_qualified_name(nr.services.cleanup_ssh), autospec=True)
|
||||
@patch("subprocess.Popen")
|
||||
def test_execute_switch_store_path_target_host(
|
||||
mock_popen: Mock,
|
||||
mock_cleanup_ssh: Mock,
|
||||
mock_run: Mock,
|
||||
mock_uuid4: Mock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
config_path = tmp_path / "test-system"
|
||||
config_path.mkdir()
|
||||
|
||||
@@ -1448,7 +1512,8 @@ def test_execute_switch_store_path_target_host(
|
||||
"-c",
|
||||
"""'exec env -i PATH="${PATH-}" LOCALE_ARCHIVE="${LOCALE_ARCHIVE-}" NIXOS_NO_CHECK="${NIXOS_NO_CHECK-}" NIXOS_INSTALL_BOOTLOADER=0 "$@"'""",
|
||||
"sh",
|
||||
*nr.nix.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
*nr.nix.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={nr.nix.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
str(config_path / "bin/switch-to-configuration"),
|
||||
"switch",
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
import textwrap
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, CompletedProcess
|
||||
from subprocess import PIPE, CompletedProcess, Popen
|
||||
from typing import Any
|
||||
from unittest.mock import ANY, Mock, call, patch
|
||||
|
||||
@@ -714,139 +714,159 @@ def test_set_profile(mock_run: Mock) -> None:
|
||||
|
||||
|
||||
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
|
||||
@patch(get_qualified_name(n.run_wrapper_bg, n), autospec=True)
|
||||
def test_switch_to_configuration_without_systemd_run(
|
||||
mock_run: Any, monkeypatch: MonkeyPatch
|
||||
mock_run_bg: Mock, mock_run: Mock, monkeypatch: MonkeyPatch
|
||||
) -> None:
|
||||
profile_path = Path("/path/to/profile")
|
||||
config_path = Path("/path/to/config")
|
||||
mock_run.return_value = CompletedProcess([], 1)
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "")
|
||||
proc = Popen(["echo"])
|
||||
try:
|
||||
mock_run_bg.return_value = p
|
||||
mock_run.return_value = CompletedProcess([], 1)
|
||||
|
||||
n.switch_to_configuration(
|
||||
profile_path,
|
||||
m.Action.SWITCH,
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "")
|
||||
|
||||
n.switch_to_configuration(
|
||||
profile_path,
|
||||
m.Action.SWITCH,
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation=None,
|
||||
install_bootloader=False,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[profile_path / "bin/switch-to-configuration", "switch"],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "0",
|
||||
},
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation=None,
|
||||
install_bootloader=False,
|
||||
remote=None,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[profile_path / "bin/switch-to-configuration", "switch"],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "0",
|
||||
},
|
||||
sudo=False,
|
||||
remote=None,
|
||||
)
|
||||
|
||||
with pytest.raises(m.NixOSRebuildError) as e:
|
||||
n.switch_to_configuration(
|
||||
config_path,
|
||||
m.Action.BOOT,
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation="special",
|
||||
with pytest.raises(m.NixOSRebuildError) as e:
|
||||
n.switch_to_configuration(
|
||||
config_path,
|
||||
m.Action.BOOT,
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation="special",
|
||||
)
|
||||
assert (
|
||||
str(e.value)
|
||||
== "error: '--specialisation' can only be used with 'switch' and 'test'"
|
||||
)
|
||||
assert (
|
||||
str(e.value)
|
||||
== "error: '--specialisation' can only be used with 'switch' and 'test'"
|
||||
)
|
||||
|
||||
target_host = m.Remote("user@localhost", [], None)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "/path/to/locale")
|
||||
mp.setenv("PATH", "/path/to/bin")
|
||||
mp.setattr(Path, Path.exists.__name__, lambda self: True)
|
||||
target_host = m.Remote("user@localhost", [], None)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "/path/to/locale")
|
||||
mp.setenv("PATH", "/path/to/bin")
|
||||
mp.setattr(Path, Path.exists.__name__, lambda self: True)
|
||||
|
||||
n.switch_to_configuration(
|
||||
Path("/path/to/config"),
|
||||
m.Action.TEST,
|
||||
n.switch_to_configuration(
|
||||
Path("/path/to/config"),
|
||||
m.Action.TEST,
|
||||
sudo=True,
|
||||
target_host=target_host,
|
||||
install_bootloader=True,
|
||||
specialisation="special",
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
config_path / "specialisation/special/bin/switch-to-configuration",
|
||||
"test",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1",
|
||||
},
|
||||
sudo=True,
|
||||
target_host=target_host,
|
||||
install_bootloader=True,
|
||||
specialisation="special",
|
||||
remote=target_host,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
config_path / "specialisation/special/bin/switch-to-configuration",
|
||||
"test",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1",
|
||||
},
|
||||
sudo=True,
|
||||
remote=target_host,
|
||||
)
|
||||
finally:
|
||||
proc.communicate(timeout=1)
|
||||
|
||||
|
||||
@patch("uuid.uuid4")
|
||||
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
|
||||
@patch(get_qualified_name(n.run_wrapper_bg, n), autospec=True)
|
||||
def test_switch_to_configuration_with_systemd_run(
|
||||
mock_run: Mock, monkeypatch: MonkeyPatch
|
||||
mock_run_bg: Mock, mock_run: Mock, mock_uuid4: Mock, monkeypatch: MonkeyPatch
|
||||
) -> None:
|
||||
test_uuid = uuid.UUID("58fe9784-f60a-42bc-aa94-eb8f1a7e5c17")
|
||||
mock_uuid4.return_value = test_uuid
|
||||
|
||||
profile_path = Path("/path/to/profile")
|
||||
config_path = Path("/path/to/config")
|
||||
mock_run.return_value = CompletedProcess([], 0)
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "")
|
||||
proc = Popen(["echo"])
|
||||
try:
|
||||
mock_run_bg.return_value = proc
|
||||
mock_run.return_value = CompletedProcess([], 0)
|
||||
|
||||
n.switch_to_configuration(
|
||||
profile_path,
|
||||
m.Action.SWITCH,
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "")
|
||||
|
||||
n.switch_to_configuration(
|
||||
profile_path,
|
||||
m.Action.SWITCH,
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation=None,
|
||||
install_bootloader=False,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
*n.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={n.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
profile_path / "bin/switch-to-configuration",
|
||||
"switch",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "0",
|
||||
},
|
||||
sudo=False,
|
||||
target_host=None,
|
||||
specialisation=None,
|
||||
install_bootloader=False,
|
||||
remote=None,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
*n.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
profile_path / "bin/switch-to-configuration",
|
||||
"switch",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "0",
|
||||
},
|
||||
sudo=False,
|
||||
remote=None,
|
||||
)
|
||||
|
||||
target_host = m.Remote("user@localhost", [], None)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "/path/to/locale")
|
||||
mp.setenv("PATH", "/path/to/bin")
|
||||
mp.setattr(Path, Path.exists.__name__, lambda self: True)
|
||||
target_host = m.Remote("user@localhost", [], None)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setenv("LOCALE_ARCHIVE", "/path/to/locale")
|
||||
mp.setenv("PATH", "/path/to/bin")
|
||||
mp.setattr(Path, Path.exists.__name__, lambda self: True)
|
||||
|
||||
n.switch_to_configuration(
|
||||
Path("/path/to/config"),
|
||||
m.Action.TEST,
|
||||
n.switch_to_configuration(
|
||||
Path("/path/to/config"),
|
||||
m.Action.TEST,
|
||||
sudo=True,
|
||||
target_host=target_host,
|
||||
install_bootloader=True,
|
||||
specialisation="special",
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
*n.SYSTEMD_RUN_CMD_PREFIX,
|
||||
f"--unit={n.SYSTEMD_RUN_UNIT_PREFIX}-{test_uuid.hex[:8]}",
|
||||
config_path / "specialisation/special/bin/switch-to-configuration",
|
||||
"test",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1",
|
||||
},
|
||||
sudo=True,
|
||||
target_host=target_host,
|
||||
install_bootloader=True,
|
||||
specialisation="special",
|
||||
remote=target_host,
|
||||
)
|
||||
mock_run.assert_called_with(
|
||||
[
|
||||
*n.SWITCH_TO_CONFIGURATION_CMD_PREFIX,
|
||||
config_path / "specialisation/special/bin/switch-to-configuration",
|
||||
"test",
|
||||
],
|
||||
env={
|
||||
"LOCALE_ARCHIVE": p.PRESERVE_ENV,
|
||||
"NIXOS_NO_CHECK": p.PRESERVE_ENV,
|
||||
"NIXOS_INSTALL_BOOTLOADER": "1",
|
||||
},
|
||||
sudo=True,
|
||||
remote=target_host,
|
||||
)
|
||||
finally:
|
||||
proc.communicate(timeout=1)
|
||||
|
||||
|
||||
@patch(
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "ruff";
|
||||
version = "0.15.4";
|
||||
version = "0.15.5";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "astral-sh";
|
||||
repo = "ruff";
|
||||
tag = finalAttrs.version;
|
||||
hash = "sha256-Vj/4Iod9aFuDK5B6cVe03/3VI5gltoWwWH/0NWrldaw=";
|
||||
hash = "sha256-bemgVXV/Bkp0aXmWX+R6Aas2/naOx0XEGp0ofh+vyyM=";
|
||||
};
|
||||
|
||||
cargoBuildFlags = [ "--package=ruff" ];
|
||||
|
||||
cargoHash = "sha256-yZeBev0vRsNXTdjQdLIkGAYIu66Yuzf6Pjct4xswXME=";
|
||||
cargoHash = "sha256-NaWWX6EAVkEg/KQ+Up0t2fh/24fnTo6i5dDZoOWErjg=";
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@ let
|
||||
# Enable Hyper-V Synthetic DRM Driver
|
||||
DRM_HYPERV = whenAtLeast "5.14" module;
|
||||
# And disable the legacy framebuffer driver when we have the new one
|
||||
FB_HYPERV = whenAtLeast "5.14" no;
|
||||
FB_HYPERV = whenBetween "5.14" "7.0" no;
|
||||
}
|
||||
// lib.optionalAttrs (stdenv.hostPlatform.system == "x86_64-linux") {
|
||||
# Intel GVT-g graphics virtualization supports 64-bit only
|
||||
@@ -739,7 +739,9 @@ let
|
||||
NFS_FSCACHE = yes;
|
||||
NFS_SWAP = yes;
|
||||
NFS_V3_ACL = yes;
|
||||
NFS_V4_1 = yes; # NFSv4.1 client support
|
||||
# NFSv4.1 is enabled unconditionally on 7.0 and up
|
||||
# see: https://github.com/torvalds/linux/commit/7537db24806fdc3d3ec4fef53babdc22c9219e75
|
||||
NFS_V4_1 = whenOlder "7.0" yes;
|
||||
NFS_V4_2 = yes;
|
||||
NFS_V4_SECURITY_LABEL = yes;
|
||||
NFS_LOCALIO = whenAtLeast "6.12" yes;
|
||||
@@ -1168,7 +1170,7 @@ let
|
||||
|
||||
ACCESSIBILITY = yes; # Accessibility support
|
||||
AUXDISPLAY = yes; # Auxiliary Display support
|
||||
HIPPI = yes;
|
||||
HIPPI = whenOlder "7.0" yes;
|
||||
MTD_COMPLEX_MAPPINGS = yes; # needed for many devices
|
||||
|
||||
SCSI_LOWLEVEL = yes; # enable lots of SCSI devices
|
||||
@@ -1378,8 +1380,14 @@ let
|
||||
HMM_MIRROR = yes;
|
||||
DRM_AMDGPU_USERPTR = yes;
|
||||
|
||||
# We want to prefer PREEMPT_LAZY when available, and fall back on PREEMPT_VOLUNTARY.
|
||||
# It just so happens that kconfig asks for PREEMPT_LAZY first, so doing it like this
|
||||
# does what we want.
|
||||
# FIXME: This is stupid and bad.
|
||||
# See: https://github.com/torvalds/linux/commit/7dadeaa6e851e7d67733f3e24fc53ee107781d0f
|
||||
PREEMPT = no;
|
||||
PREEMPT_VOLUNTARY = yes;
|
||||
PREEMPT_LAZY = option yes;
|
||||
PREEMPT_VOLUNTARY = option yes;
|
||||
|
||||
X86_AMD_PLATFORM_DEVICE = lib.mkIf stdenv.hostPlatform.isx86 yes;
|
||||
X86_PLATFORM_DRIVERS_DELL = lib.mkIf stdenv.hostPlatform.isx86 (whenAtLeast "5.12" yes);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"testing": {
|
||||
"version": "6.19-rc8",
|
||||
"hash": "sha256:1vwlhsk3cry48p9v2kqqxkmrsw2sjgspfylv3c7r5430yk7fhyg1",
|
||||
"version": "7.0-rc2",
|
||||
"hash": "sha256:11zw5rck5hg1w8jjjgc8hjmswzdv4k1wqnkg4zmzxg0ds447cd0b",
|
||||
"lts": false
|
||||
},
|
||||
"6.1": {
|
||||
"version": "6.1.165",
|
||||
"hash": "sha256:1x1fsaax7yd5p3vwcgp04hbcafmwfqiw1bz0qxsip45yy62730as",
|
||||
"version": "6.1.166",
|
||||
"hash": "sha256:0jcl12gjlfdf9pwqg1m84rzwnrj3grxxgk5blrq8zlaq45sgr3c1",
|
||||
"lts": true
|
||||
},
|
||||
"5.15": {
|
||||
@@ -20,13 +20,13 @@
|
||||
"lts": true
|
||||
},
|
||||
"6.6": {
|
||||
"version": "6.6.128",
|
||||
"hash": "sha256:0j4f6imsxm5pyqk2yn4snv47q272dwpzp0w8qcaiy0l0hjxk75k6",
|
||||
"version": "6.6.129",
|
||||
"hash": "sha256:12j42awg44w97zq8fzifpm300jm9q9ya7qkpn7xbnkr2480qz86a",
|
||||
"lts": true
|
||||
},
|
||||
"6.12": {
|
||||
"version": "6.12.75",
|
||||
"hash": "sha256:0xy1q4x18z6ghaw481hqvkmnkcgxn00nb0n4224amwbgalkpkvh6",
|
||||
"version": "6.12.76",
|
||||
"hash": "sha256:15nq7agr492b9lh57xp10hs48dx9g7k253y2lm4vvrj69j1kxd5v",
|
||||
"lts": true
|
||||
},
|
||||
"6.18": {
|
||||
|
||||
@@ -50,7 +50,6 @@ assert lib.assertMsg (
|
||||
lsof,
|
||||
mercurial,
|
||||
mdbook,
|
||||
mdbook-linkcheck,
|
||||
nlohmann_json,
|
||||
ninja,
|
||||
openssl,
|
||||
@@ -179,7 +178,6 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
++ lib.optionals enableDocumentation [
|
||||
(lib.getBin lowdown-unsandboxed)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
doxygen
|
||||
]
|
||||
++ lib.optionals (hasDtraceSupport && withDtrace) [ systemtap-sdt ]
|
||||
|
||||
@@ -45,7 +45,6 @@ assert (hash == null) -> (src != null);
|
||||
meson,
|
||||
ninja,
|
||||
mdbook,
|
||||
mdbook-linkcheck,
|
||||
nlohmann_json,
|
||||
nixosTests,
|
||||
openssl,
|
||||
@@ -116,7 +115,6 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
++ lib.optionals enableDocumentation [
|
||||
(lib.getBin lowdown-unsandboxed)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
util-linuxMinimal
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
ninja,
|
||||
lowdown-unsandboxed,
|
||||
mdbook,
|
||||
mdbook-linkcheck,
|
||||
jq,
|
||||
python3,
|
||||
rsync,
|
||||
@@ -35,7 +34,6 @@ mkMesonDerivation (finalAttrs: {
|
||||
ninja
|
||||
(lib.getBin lowdown-unsandboxed)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
jq
|
||||
python3
|
||||
rsync
|
||||
|
||||
@@ -1298,6 +1298,7 @@ mapAliases {
|
||||
matrix-synapse-tools.synadm = throw "'matrix-synapse-tools.synadm' has been renamed to/replaced by 'synadm'"; # Converted to throw 2025-10-27
|
||||
mcomix3 = throw "'mcomix3' has been renamed to/replaced by 'mcomix'"; # Converted to throw 2025-10-27
|
||||
mdbook-alerts = throw "'mdbook-alerts' has been removed because it is deprecated and natively supported by mdbook since version 0.5.0"; # Added 2026-01-07
|
||||
mdbook-linkcheck = throw "'mdbook-linkcheck' has been removed and replaced by 'mdbook-linkcheck2' due to incompatibility with mdbook version 0.5.0+"; # Added 2026-03-03
|
||||
mdt = throw "'mdt' has been renamed to/replaced by 'md-tui'"; # Converted to throw 2025-10-27
|
||||
mediastreamer = throw "'mediastreamer' has been moved to 'linphonePackages.mediastreamer2'"; # Added 2025-09-20
|
||||
mediastreamer-openh264 = throw "'mediastreamer-openh264' has been moved to 'linphonePackages.msopenh264'"; # Added 2025-09-20
|
||||
|
||||
Reference in New Issue
Block a user