staging-nixos merge for 2025-11-12 (#460995)
This commit is contained in:
@@ -397,6 +397,8 @@ and [release notes for v18](https://goteleport.com/docs/changelog/#1800-070325).
|
|||||||
- In all other cases, you'll need to set this option to `true` yourself.
|
- In all other cases, you'll need to set this option to `true` yourself.
|
||||||
- `boot.isNspawnContainer` being `true` implies [](#opt-boot.isContainer) being `true`.
|
- `boot.isNspawnContainer` being `true` implies [](#opt-boot.isContainer) being `true`.
|
||||||
|
|
||||||
|
- `users.users.*.linger` now defaults to `null` rather than `false`, meaning NixOS will not attempt to enable or disable lingering for that user account, instead allowing for imperative control over lingering using the `loginctl` commands. In practice, this is unlikely to make a difference for most people, as new users are created without lingering configured. There is a new, related option, `users.manageLingering`, which can be used to prevent NixOS attempting to manage lingering entirely.
|
||||||
|
|
||||||
- Due to [deprecation of gnome-session X11 support](https://blogs.gnome.org/alatiera/2025/06/08/the-x11-session-removal/), `services.desktopManager.pantheon` now defaults to pantheon-wayland session. The X11 session has been removed, see [this issue](https://github.com/elementary/session-settings/issues/91) for details.
|
- Due to [deprecation of gnome-session X11 support](https://blogs.gnome.org/alatiera/2025/06/08/the-x11-session-removal/), `services.desktopManager.pantheon` now defaults to pantheon-wayland session. The X11 session has been removed, see [this issue](https://github.com/elementary/session-settings/issues/91) for details.
|
||||||
|
|
||||||
- `bcachefs` file systems will now use the out-of-tree module for supported kernels. The in-tree module has been removed, and users will need to switch to kernels that support the out-of-tree module.
|
- `bcachefs` file systems will now use the out-of-tree module for supported kernels. The in-tree module has been removed, and users will need to switch to kernels that support the out-of-tree module.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ let
|
|||||||
any
|
any
|
||||||
attrNames
|
attrNames
|
||||||
attrValues
|
attrValues
|
||||||
|
boolToString
|
||||||
concatMap
|
concatMap
|
||||||
concatMapStringsSep
|
concatMapStringsSep
|
||||||
concatStrings
|
concatStrings
|
||||||
@@ -43,6 +44,7 @@ let
|
|||||||
stringLength
|
stringLength
|
||||||
trace
|
trace
|
||||||
types
|
types
|
||||||
|
versionOlder
|
||||||
xor
|
xor
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -455,16 +457,21 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
linger = mkOption {
|
linger = mkOption {
|
||||||
type = types.bool;
|
type = types.nullOr types.bool;
|
||||||
default = false;
|
example = true;
|
||||||
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
Whether to enable lingering for this user. If true, systemd user
|
Whether to enable or disable lingering for this user. Without
|
||||||
units will start at boot, rather than starting at login and stopping
|
lingering, user units will not be started until the user logs in,
|
||||||
at logout. This is the declarative equivalent of running
|
and may be stopped on logout depending on the settings in
|
||||||
`loginctl enable-linger` for this user.
|
`logind.conf`.
|
||||||
|
|
||||||
If false, user units will not be started until the user logs in, and
|
By default, NixOS will not manage lingering, new users will default
|
||||||
may be stopped on logout depending on the settings in `logind.conf`.
|
to not lingering, and lingering can be configured imperatively using
|
||||||
|
`loginctl enable-linger` or `loginctl disable-linger`. Setting
|
||||||
|
this option to `true` or `false` is the declarative equivalent of
|
||||||
|
running `loginctl enable-linger` or `loginctl disable-linger`
|
||||||
|
respectively.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -661,8 +668,6 @@ let
|
|||||||
shells = mapAttrsToList (_: u: u.shell) cfg.users;
|
shells = mapAttrsToList (_: u: u.shell) cfg.users;
|
||||||
in
|
in
|
||||||
filter types.shellPackage.check shells;
|
filter types.shellPackage.check shells;
|
||||||
|
|
||||||
lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
@@ -710,6 +715,13 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
users.manageLingering = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Whether to manage whether users linger or not.";
|
||||||
|
example = false;
|
||||||
|
};
|
||||||
|
|
||||||
users.users = mkOption {
|
users.users = mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = with types; attrsOf (submodule userOpts);
|
type = with types; attrsOf (submodule userOpts);
|
||||||
@@ -894,32 +906,52 @@ in
|
|||||||
else
|
else
|
||||||
""; # keep around for backwards compatibility
|
""; # keep around for backwards compatibility
|
||||||
|
|
||||||
systemd.services.linger-users = lib.mkIf ((length lingeringUsers) > 0) {
|
systemd.services.linger-users = lib.mkIf cfg.manageLingering {
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "systemd-logind.service" ];
|
after = [ "systemd-logind.service" ];
|
||||||
requires = [ "systemd-logind.service" ];
|
requires = [ "systemd-logind.service" ];
|
||||||
|
|
||||||
script =
|
script =
|
||||||
let
|
let
|
||||||
lingerDir = "/var/lib/systemd/linger";
|
lingeringUsers = filterAttrs (n: v: v.linger == true) cfg.users;
|
||||||
lingeringUsersFile = builtins.toFile "lingering-users" (
|
nonLingeringUsers = filterAttrs (n: v: v.linger == false) cfg.users;
|
||||||
concatStrings (map (s: "${s}\n") (sort (a: b: a < b) lingeringUsers))
|
lingeringUserNames = mapAttrsToList (n: v: v.name) lingeringUsers;
|
||||||
); # this sorting is important for `comm` to work correctly
|
nonLingeringUserNames = mapAttrsToList (n: v: v.name) nonLingeringUsers;
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
mkdir -vp ${lingerDir}
|
${lib.strings.toShellVars { inherit lingeringUserNames nonLingeringUserNames; }}
|
||||||
cd ${lingerDir}
|
|
||||||
for user in $(ls); do
|
user_configured () {
|
||||||
if ! id "$user" >/dev/null; then
|
# Use `id` to check if the user exists rather than checking the
|
||||||
echo "Removing linger for missing user $user"
|
# NixOS configuration, as it may be that the user has been
|
||||||
rm --force -- "$user"
|
# manually configured, which is permitted if users.mutableUsers
|
||||||
fi
|
# is true (the default).
|
||||||
|
id "$1" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
shopt -s dotglob nullglob
|
||||||
|
for user in *; do
|
||||||
|
if ! user_configured "$user"; then
|
||||||
|
# systemd has this user configured to linger despite them not
|
||||||
|
# existing.
|
||||||
|
echo "Removing linger for missing user $user" >&2
|
||||||
|
rm -- "$user"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
ls | sort | comm -3 -1 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl disable-linger
|
|
||||||
ls | sort | comm -3 -2 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl enable-linger
|
if (( ''${#nonLingeringUserNames[*]} > 0 )); then
|
||||||
|
${config.systemd.package}/bin/loginctl disable-linger "''${nonLingeringUserNames[@]}"
|
||||||
|
fi
|
||||||
|
if (( ''${#lingeringUserNames[*]} > 0 )); then
|
||||||
|
${config.systemd.package}/bin/loginctl enable-linger "''${lingeringUserNames[@]}"
|
||||||
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
serviceConfig.Type = "oneshot";
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
StateDirectory = "systemd/linger";
|
||||||
|
WorkingDirectory = "/var/lib/systemd/linger";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Warn about user accounts with deprecated password hashing schemes
|
# Warn about user accounts with deprecated password hashing schemes
|
||||||
@@ -1163,6 +1195,22 @@ in
|
|||||||
users.groups.${user.name} = {};
|
users.groups.${user.name} = {};
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
assertion = user.linger != null -> cfg.manageLingering;
|
||||||
|
message = ''
|
||||||
|
users.manageLingering is set to false, but
|
||||||
|
users.users.${user.name}.linger is configured.
|
||||||
|
|
||||||
|
If you want NixOS to manage whether user accounts linger or
|
||||||
|
not, you must set users.manageLingering to true. This is the
|
||||||
|
default setting.
|
||||||
|
|
||||||
|
If you do not want NixOS to manage whether user accounts linger
|
||||||
|
or not, you must set users.users.${user.name}.linger to null.
|
||||||
|
This is the default setting provided system.stateVersion is at
|
||||||
|
least "25.11".
|
||||||
|
'';
|
||||||
|
}
|
||||||
]
|
]
|
||||||
++ (map
|
++ (map
|
||||||
(shell: {
|
(shell: {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
boot.kexec.enable = lib.mkDefault false;
|
boot.kexec.enable = lib.mkDefault false;
|
||||||
# Relies on bash scripts
|
# Relies on bash scripts
|
||||||
powerManagement.enable = lib.mkDefault false;
|
powerManagement.enable = lib.mkDefault false;
|
||||||
|
users.manageLingering = lib.mkDefault false;
|
||||||
# Relies on the gzip command which depends on bash
|
# Relies on the gzip command which depends on bash
|
||||||
services.logrotate.enable = lib.mkDefault false;
|
services.logrotate.enable = lib.mkDefault false;
|
||||||
|
|
||||||
|
|||||||
@@ -1522,6 +1522,7 @@ in
|
|||||||
systemd-sysusers-password-option-override-ordering = runTest ./systemd-sysusers-password-option-override-ordering.nix;
|
systemd-sysusers-password-option-override-ordering = runTest ./systemd-sysusers-password-option-override-ordering.nix;
|
||||||
systemd-timesyncd-nscd-dnssec = runTest ./systemd-timesyncd-nscd-dnssec.nix;
|
systemd-timesyncd-nscd-dnssec = runTest ./systemd-timesyncd-nscd-dnssec.nix;
|
||||||
systemd-user-linger = runTest ./systemd-user-linger.nix;
|
systemd-user-linger = runTest ./systemd-user-linger.nix;
|
||||||
|
systemd-user-linger-purge = runTest ./systemd-user-linger-purge.nix;
|
||||||
systemd-user-tmpfiles-rules = runTest ./systemd-user-tmpfiles-rules.nix;
|
systemd-user-tmpfiles-rules = runTest ./systemd-user-tmpfiles-rules.nix;
|
||||||
systemd-userdbd = runTest ./systemd-userdbd.nix;
|
systemd-userdbd = runTest ./systemd-userdbd.nix;
|
||||||
systemtap = handleTest ./systemtap.nix { };
|
systemtap = handleTest ./systemtap.nix { };
|
||||||
|
|||||||
37
nixos/tests/systemd-user-linger-purge.nix
Normal file
37
nixos/tests/systemd-user-linger-purge.nix
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# This test checks #418101, where lingering users would not be cleared up if
|
||||||
|
# the configuration is updated to remove lingering from all users.
|
||||||
|
rec {
|
||||||
|
name = "systemd-user-linger-purge";
|
||||||
|
|
||||||
|
nodes.machine = {
|
||||||
|
users.users = {
|
||||||
|
bob = {
|
||||||
|
isNormalUser = true;
|
||||||
|
linger = false;
|
||||||
|
uid = 1001;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
let
|
||||||
|
uidStrings = builtins.mapAttrs (k: v: builtins.toString v.uid) nodes.machine.users.users;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
machine.fail("test -e /var/lib/systemd/linger/bob")
|
||||||
|
machine.fail("systemctl status user-${uidStrings.bob}.slice")
|
||||||
|
|
||||||
|
with subtest("missing users have linger purged"):
|
||||||
|
machine.succeed("touch /var/lib/systemd/linger/alice")
|
||||||
|
machine.systemctl("restart linger-users")
|
||||||
|
machine.succeed("test ! -e /var/lib/systemd/linger/alice")
|
||||||
|
|
||||||
|
with subtest("mutable users can linger"):
|
||||||
|
machine.succeed("useradd alice")
|
||||||
|
machine.succeed("test ! -e /var/lib/systemd/linger/alice")
|
||||||
|
machine.succeed("loginctl enable-linger alice")
|
||||||
|
machine.succeed("test -e /var/lib/systemd/linger/alice")
|
||||||
|
machine.systemctl("restart linger-users")
|
||||||
|
machine.succeed("test -e /var/lib/systemd/linger/alice")
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -1,37 +1,39 @@
|
|||||||
{ lib, ... }:
|
rec {
|
||||||
{
|
|
||||||
name = "systemd-user-linger";
|
name = "systemd-user-linger";
|
||||||
|
|
||||||
nodes.machine =
|
nodes.machine = {
|
||||||
{ ... }:
|
users.users = {
|
||||||
{
|
alice = {
|
||||||
users.users = {
|
isNormalUser = true;
|
||||||
alice = {
|
linger = true;
|
||||||
isNormalUser = true;
|
uid = 1000;
|
||||||
linger = true;
|
};
|
||||||
uid = 1000;
|
|
||||||
};
|
|
||||||
|
|
||||||
bob = {
|
bob = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
linger = false;
|
linger = false;
|
||||||
uid = 10001;
|
uid = 1001;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
testScript =
|
testScript =
|
||||||
{ ... }:
|
let
|
||||||
|
uidStrings = builtins.mapAttrs (k: v: builtins.toString v.uid) nodes.machine.users.users;
|
||||||
|
in
|
||||||
''
|
''
|
||||||
machine.wait_for_file("/var/lib/systemd/linger/alice")
|
machine.wait_for_file("/var/lib/systemd/linger/alice")
|
||||||
machine.succeed("systemctl status user-1000.slice")
|
machine.succeed("systemctl status user-${uidStrings.alice}.slice")
|
||||||
|
|
||||||
machine.fail("test -e /var/lib/systemd/linger/bob")
|
machine.fail("test -e /var/lib/systemd/linger/bob")
|
||||||
machine.fail("systemctl status user-1001.slice")
|
machine.fail("systemctl status user-${uidStrings.bob}.slice")
|
||||||
|
|
||||||
with subtest("missing users have linger purged"):
|
with subtest("mutable users can linger"):
|
||||||
machine.succeed("touch /var/lib/systemd/linger/missing")
|
machine.succeed("useradd clare")
|
||||||
|
machine.succeed("test ! -e /var/lib/systemd/linger/clare")
|
||||||
|
machine.succeed("loginctl enable-linger clare")
|
||||||
|
machine.succeed("test -e /var/lib/systemd/linger/clare")
|
||||||
machine.systemctl("restart linger-users")
|
machine.systemctl("restart linger-users")
|
||||||
machine.succeed("test ! -e /var/lib/systemd/linger/missing")
|
machine.succeed("test -e /var/lib/systemd/linger/clare")
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "btrfs-progs";
|
pname = "btrfs-progs";
|
||||||
version = "6.17";
|
version = "6.17.1";
|
||||||
|
|
||||||
src = fetchurl {
|
src = fetchurl {
|
||||||
url = "mirror://kernel/linux/kernel/people/kdave/btrfs-progs/btrfs-progs-v${version}.tar.xz";
|
url = "mirror://kernel/linux/kernel/people/kdave/btrfs-progs/btrfs-progs-v${version}.tar.xz";
|
||||||
hash = "sha256-J31pbJ15cT/1r7U8fv69zq0uamAHeJsXQuxBH05Moik=";
|
hash = "sha256-pL4Kbrs8R2Qn+12Xss8CewzNtrDFX/FjIzIMHoy3dlg=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ nixos-rebuild - reconfigure a NixOS machine
|
|||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
|
|
||||||
; document here only non-deprecated flags
|
; 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]++
|
_nixos-rebuild_ \[--verbose] [--quiet] [--max-jobs MAX_JOBS] [--cores CORES] [--log-format LOG_FORMAT] [--keep-going] [--keep-failed] [--fallback] [--repair] [--option OPTION OPTION] [--builders BUILDERS] [--include INCLUDE]++
|
||||||
\[--include INCLUDE] [--quiet] [--print-build-logs] [--show-trace] [--accept-flake-config] [--refresh] [--impure] [--offline] [--no-net] [--recreate-lock-file]++
|
\[--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]++
|
||||||
\[--no-update-lock-file] [--no-write-lock-file] [--no-registries] [--commit-lock-file] [--update-input UPDATE_INPUT] [--override-input OVERRIDE_INPUT OVERRIDE_INPUT]++
|
\[--update-input UPDATE_INPUT] [--override-input OVERRIDE_INPUT OVERRIDE_INPUT] [--no-build-output] [--use-substitutes] [--help] [--debug] [--file FILE] [--attr ATTR] [--flake [FLAKE]] [--no-flake] [--install-bootloader]++
|
||||||
\[--no-build-output] [--use-substitutes] [--help] [--file FILE] [--attr ATTR] [--flake [FLAKE]] [--no-flake] [--install-bootloader] [--profile-name PROFILE_NAME]++
|
\[--profile-name PROFILE_NAME] [--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--no-reexec]++
|
||||||
\[--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--no-reexec]++
|
\[--build-host BUILD_HOST] [--target-host TARGET_HOST] [--no-build-nix] [--image-variant IMAGE_VARIANT]++
|
||||||
\[--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}]
|
\[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-image,build-vm,build-vm-with-bootloader,list-generations}]
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
@@ -260,9 +258,10 @@ It must be one of the following:
|
|||||||
When set, *nixos-rebuild* prefixes activation commands with sudo.
|
When set, *nixos-rebuild* prefixes activation commands with sudo.
|
||||||
Setting this option allows deploying as a non-root user.
|
Setting this option allows deploying as a non-root user.
|
||||||
|
|
||||||
*--ask-sudo-password*
|
*--ask-sudo-password*, *-S*
|
||||||
When set, *nixos-rebuild* will ask for sudo password for remote
|
When set, *nixos-rebuild* will ask for sudo password for remote
|
||||||
activation (i.e.: on *--target-host*) at the start of the build process.
|
activation (i.e.: on *--target-host*) at the start of the build process.
|
||||||
|
Implies *--sudo*.
|
||||||
|
|
||||||
*--file* _path_, *-f* _path_
|
*--file* _path_, *-f* _path_
|
||||||
Enable and build the NixOS system from the specified file. The file must
|
Enable and build the NixOS system from the specified file. The file must
|
||||||
@@ -305,9 +304,9 @@ Flake-related options:
|
|||||||
Builder options:
|
Builder options:
|
||||||
|
|
||||||
*--verbose,* *-v*, *--quiet*, *--log-format*, *--no-build-output*, *-Q*,
|
*--verbose,* *-v*, *--quiet*, *--log-format*, *--no-build-output*, *-Q*,
|
||||||
*--max-jobs*, *-j*, *--cores*, *--keep-going*, *-k*, *--keep-failed*, *-K*,
|
*--no-link*, *--max-jobs*, *-j*, *--cores*, *--keep-going*, *-k*,
|
||||||
*--fallback*, *--include*, *-I*, *--option*, *--repair*, *--builders*,
|
*--keep-failed*, *-K*, *--fallback*, *--include*, *-I*, *--option*, *--repair*,
|
||||||
*--print-build-logs*, *-L*, *--show-trace*
|
*--builders*, *--print-build-logs*, *-L*, *--show-trace*
|
||||||
|
|
||||||
See the Nix manual, *nix flake lock --help* or *nix-build --help* for details.
|
See the Nix manual, *nix flake lock --help* or *nix-build --help* for details.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from typing import Final, assert_never
|
|||||||
|
|
||||||
from . import nix, services
|
from . import nix, services
|
||||||
from .constants import EXECUTABLE, WITH_REEXEC, WITH_SHELL_FILES
|
from .constants import EXECUTABLE, WITH_REEXEC, WITH_SHELL_FILES
|
||||||
from .models import Action, BuildAttr, Flake, Profile
|
from .models import Action, BuildAttr, Flake, GroupedNixArgs, Profile
|
||||||
from .process import Remote
|
from .process import Remote
|
||||||
from .utils import LogFormatter
|
from .utils import LogFormatter
|
||||||
|
|
||||||
@@ -55,7 +55,9 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
|
|||||||
flake_common_flags.add_argument("--override-input", nargs=2, action="append")
|
flake_common_flags.add_argument("--override-input", nargs=2, action="append")
|
||||||
|
|
||||||
classic_build_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
classic_build_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
||||||
classic_build_flags.add_argument("--no-build-output", "-Q", action="store_true")
|
classic_build_flags.add_argument(
|
||||||
|
"--no-build-output", "--no-link", "-Q", action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
copy_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
copy_flags = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
||||||
copy_flags.add_argument(
|
copy_flags.add_argument(
|
||||||
@@ -151,6 +153,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
|
|||||||
)
|
)
|
||||||
main_parser.add_argument(
|
main_parser.add_argument(
|
||||||
"--ask-sudo-password",
|
"--ask-sudo-password",
|
||||||
|
"-S",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Asks for sudo password for remote activation, implies --sudo",
|
help="Asks for sudo password for remote activation, implies --sudo",
|
||||||
)
|
)
|
||||||
@@ -195,13 +198,15 @@ def get_main_parser() -> argparse.ArgumentParser:
|
|||||||
|
|
||||||
def parse_args(
|
def parse_args(
|
||||||
argv: list[str],
|
argv: list[str],
|
||||||
) -> tuple[argparse.Namespace, dict[str, argparse.Namespace]]:
|
) -> tuple[argparse.Namespace, GroupedNixArgs]:
|
||||||
parser, sub_parsers = get_parser()
|
parser, sub_parsers = get_parser()
|
||||||
args = parser.parse_args(argv[1:])
|
args = parser.parse_args(argv[1:])
|
||||||
args_groups = {
|
grouped_nix_args = GroupedNixArgs.from_parsed_args_groups(
|
||||||
group: parser.parse_known_args(argv[1:])[0]
|
{
|
||||||
for group, parser in sub_parsers.items()
|
group: parser.parse_known_args(argv[1:])[0]
|
||||||
}
|
for group, parser in sub_parsers.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if args.help or args.action is None:
|
if args.help or args.action is None:
|
||||||
if WITH_SHELL_FILES:
|
if WITH_SHELL_FILES:
|
||||||
@@ -263,18 +268,11 @@ def parse_args(
|
|||||||
if args.flake and (args.file or args.attr):
|
if args.flake and (args.file or args.attr):
|
||||||
parser.error("--flake cannot be used with --file or --attr")
|
parser.error("--flake cannot be used with --file or --attr")
|
||||||
|
|
||||||
return args, args_groups
|
return args, grouped_nix_args
|
||||||
|
|
||||||
|
|
||||||
def execute(argv: list[str]) -> None:
|
def execute(argv: list[str]) -> None:
|
||||||
args, args_groups = parse_args(argv)
|
args, grouped_nix_args = parse_args(argv)
|
||||||
|
|
||||||
common_flags = vars(args_groups["common_flags"])
|
|
||||||
common_build_flags = common_flags | vars(args_groups["common_build_flags"])
|
|
||||||
build_flags = common_build_flags | vars(args_groups["classic_build_flags"])
|
|
||||||
flake_common_flags = common_flags | vars(args_groups["flake_common_flags"])
|
|
||||||
flake_build_flags = common_build_flags | flake_common_flags
|
|
||||||
copy_flags = common_flags | vars(args_groups["copy_flags"])
|
|
||||||
|
|
||||||
if args.upgrade or args.upgrade_all:
|
if args.upgrade or args.upgrade_all:
|
||||||
nix.upgrade_channels(args.upgrade_all, args.sudo)
|
nix.upgrade_channels(args.upgrade_all, args.sudo)
|
||||||
@@ -290,7 +288,7 @@ def execute(argv: list[str]) -> None:
|
|||||||
# Re-exec to a newer version of the script before building to ensure we get
|
# Re-exec to a newer version of the script before building to ensure we get
|
||||||
# the latest fixes
|
# the latest fixes
|
||||||
if WITH_REEXEC and can_run and not args.no_reexec:
|
if WITH_REEXEC and can_run and not args.no_reexec:
|
||||||
services.reexec(argv, args, build_flags, flake_build_flags)
|
services.reexec(argv, args, grouped_nix_args)
|
||||||
|
|
||||||
profile = Profile.from_arg(args.profile_name)
|
profile = Profile.from_arg(args.profile_name)
|
||||||
target_host = Remote.from_arg(args.target_host, args.ask_sudo_password)
|
target_host = Remote.from_arg(args.target_host, args.ask_sudo_password)
|
||||||
@@ -299,7 +297,7 @@ def execute(argv: list[str]) -> None:
|
|||||||
flake = Flake.from_arg(args.flake, target_host)
|
flake = Flake.from_arg(args.flake, target_host)
|
||||||
|
|
||||||
if can_run and not flake:
|
if can_run and not flake:
|
||||||
services.write_version_suffix(build_flags)
|
services.write_version_suffix(grouped_nix_args)
|
||||||
|
|
||||||
match action:
|
match action:
|
||||||
case (
|
case (
|
||||||
@@ -321,15 +319,11 @@ def execute(argv: list[str]) -> None:
|
|||||||
profile=profile,
|
profile=profile,
|
||||||
flake=flake,
|
flake=flake,
|
||||||
build_attr=build_attr,
|
build_attr=build_attr,
|
||||||
build_flags=build_flags,
|
grouped_nix_args=grouped_nix_args,
|
||||||
common_flags=common_flags,
|
|
||||||
copy_flags=copy_flags,
|
|
||||||
flake_build_flags=flake_build_flags,
|
|
||||||
flake_common_flags=flake_common_flags,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
case Action.EDIT:
|
case Action.EDIT:
|
||||||
services.edit(flake=flake, flake_build_flags=flake_build_flags)
|
services.edit(flake=flake, grouped_nix_args=grouped_nix_args)
|
||||||
|
|
||||||
case Action.DRY_RUN:
|
case Action.DRY_RUN:
|
||||||
raise AssertionError("DRY_RUN should be a DRY_BUILD alias")
|
raise AssertionError("DRY_RUN should be a DRY_BUILD alias")
|
||||||
@@ -341,8 +335,7 @@ def execute(argv: list[str]) -> None:
|
|||||||
services.repl(
|
services.repl(
|
||||||
flake=flake,
|
flake=flake,
|
||||||
build_attr=build_attr,
|
build_attr=build_attr,
|
||||||
flake_build_flags=flake_build_flags,
|
grouped_nix_args=grouped_nix_args,
|
||||||
build_flags=build_flags,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, ClassVar, Self, TypedDict, override
|
from typing import Any, ClassVar, Self, TypedDict, override
|
||||||
|
|
||||||
from .process import Remote, run_wrapper
|
from .process import Remote, run_wrapper
|
||||||
|
from .utils import Args
|
||||||
|
|
||||||
type ImageVariants = dict[str, str]
|
type ImageVariants = dict[str, str]
|
||||||
|
|
||||||
@@ -143,6 +145,35 @@ class GenerationJson(TypedDict):
|
|||||||
current: bool
|
current: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class GroupedNixArgs:
|
||||||
|
build_flags: Args
|
||||||
|
common_flags: Args
|
||||||
|
copy_flags: Args
|
||||||
|
flake_build_flags: Args
|
||||||
|
flake_common_flags: Args
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_parsed_args_groups(cls, args_groups: dict[str, Namespace]) -> Self:
|
||||||
|
common_flags = vars(args_groups["common_flags"])
|
||||||
|
common_build_flags = common_flags | vars(args_groups["common_build_flags"])
|
||||||
|
build_flags = common_build_flags | vars(args_groups["classic_build_flags"])
|
||||||
|
flake_common_flags = common_flags | vars(args_groups["flake_common_flags"])
|
||||||
|
flake_build_flags = common_build_flags | flake_common_flags
|
||||||
|
copy_flags = common_flags | vars(args_groups["copy_flags"])
|
||||||
|
# --no-build-output -> --no-link
|
||||||
|
if build_flags.get("no_build_output"):
|
||||||
|
flake_build_flags["no_link"] = True
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
build_flags=build_flags,
|
||||||
|
common_flags=common_flags,
|
||||||
|
copy_flags=copy_flags,
|
||||||
|
flake_build_flags=flake_build_flags,
|
||||||
|
flake_common_flags=flake_common_flags,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Profile:
|
class Profile:
|
||||||
name: str
|
name: str
|
||||||
|
|||||||
@@ -8,9 +8,17 @@ from typing import Final
|
|||||||
|
|
||||||
from . import nix, tmpdir
|
from . import nix, tmpdir
|
||||||
from .constants import EXECUTABLE
|
from .constants import EXECUTABLE
|
||||||
from .models import Action, BuildAttr, Flake, ImageVariants, NixOSRebuildError, Profile
|
from .models import (
|
||||||
|
Action,
|
||||||
|
BuildAttr,
|
||||||
|
Flake,
|
||||||
|
GroupedNixArgs,
|
||||||
|
ImageVariants,
|
||||||
|
NixOSRebuildError,
|
||||||
|
Profile,
|
||||||
|
)
|
||||||
from .process import Remote, cleanup_ssh
|
from .process import Remote, cleanup_ssh
|
||||||
from .utils import Args, tabulate
|
from .utils import tabulate
|
||||||
|
|
||||||
NIXOS_REBUILD_ATTR: Final = "config.system.build.nixos-rebuild"
|
NIXOS_REBUILD_ATTR: Final = "config.system.build.nixos-rebuild"
|
||||||
NIXOS_REBUILD_REEXEC_ENV: Final = "_NIXOS_REBUILD_REEXEC"
|
NIXOS_REBUILD_REEXEC_ENV: Final = "_NIXOS_REBUILD_REEXEC"
|
||||||
@@ -21,8 +29,7 @@ logger: Final = logging.getLogger(__name__)
|
|||||||
def reexec(
|
def reexec(
|
||||||
argv: list[str],
|
argv: list[str],
|
||||||
args: argparse.Namespace,
|
args: argparse.Namespace,
|
||||||
build_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
flake_build_flags: Args,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
if os.environ.get(NIXOS_REBUILD_REEXEC_ENV):
|
if os.environ.get(NIXOS_REBUILD_REEXEC_ENV):
|
||||||
return
|
return
|
||||||
@@ -36,14 +43,14 @@ def reexec(
|
|||||||
drv = nix.build_flake(
|
drv = nix.build_flake(
|
||||||
NIXOS_REBUILD_ATTR,
|
NIXOS_REBUILD_ATTR,
|
||||||
flake,
|
flake,
|
||||||
flake_build_flags | {"no_link": True},
|
grouped_nix_args.flake_build_flags | {"no_link": True},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
build_attr = BuildAttr.from_arg(args.attr, args.file)
|
build_attr = BuildAttr.from_arg(args.attr, args.file)
|
||||||
drv = nix.build(
|
drv = nix.build(
|
||||||
NIXOS_REBUILD_ATTR,
|
NIXOS_REBUILD_ATTR,
|
||||||
build_attr,
|
build_attr,
|
||||||
build_flags | {"no_out_link": True},
|
grouped_nix_args.build_flags | {"no_out_link": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
if drv:
|
if drv:
|
||||||
@@ -88,21 +95,20 @@ def _get_system_attr(
|
|||||||
args: argparse.Namespace,
|
args: argparse.Namespace,
|
||||||
flake: Flake | None,
|
flake: Flake | None,
|
||||||
build_attr: BuildAttr,
|
build_attr: BuildAttr,
|
||||||
common_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
flake_common_flags: Args,
|
|
||||||
) -> str:
|
) -> str:
|
||||||
match action:
|
match action:
|
||||||
case Action.BUILD_IMAGE if flake:
|
case Action.BUILD_IMAGE if flake:
|
||||||
variants = nix.get_build_image_variants_flake(
|
variants = nix.get_build_image_variants_flake(
|
||||||
flake,
|
flake,
|
||||||
eval_flags=flake_common_flags,
|
eval_flags=grouped_nix_args.flake_common_flags,
|
||||||
)
|
)
|
||||||
_validate_image_variant(args.image_variant, variants)
|
_validate_image_variant(args.image_variant, variants)
|
||||||
attr = f"config.system.build.images.{args.image_variant}"
|
attr = f"config.system.build.images.{args.image_variant}"
|
||||||
case Action.BUILD_IMAGE:
|
case Action.BUILD_IMAGE:
|
||||||
variants = nix.get_build_image_variants(
|
variants = nix.get_build_image_variants(
|
||||||
build_attr,
|
build_attr,
|
||||||
instantiate_flags=common_flags,
|
instantiate_flags=grouped_nix_args.common_flags,
|
||||||
)
|
)
|
||||||
_validate_image_variant(args.image_variant, variants)
|
_validate_image_variant(args.image_variant, variants)
|
||||||
attr = f"config.system.build.images.{args.image_variant}"
|
attr = f"config.system.build.images.{args.image_variant}"
|
||||||
@@ -146,11 +152,7 @@ def _build_system(
|
|||||||
target_host: Remote | None,
|
target_host: Remote | None,
|
||||||
flake: Flake | None,
|
flake: Flake | None,
|
||||||
build_attr: BuildAttr,
|
build_attr: BuildAttr,
|
||||||
build_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
common_flags: Args,
|
|
||||||
copy_flags: Args,
|
|
||||||
flake_build_flags: Args,
|
|
||||||
flake_common_flags: Args,
|
|
||||||
) -> Path:
|
) -> Path:
|
||||||
dry_run = action == Action.DRY_BUILD
|
dry_run = action == Action.DRY_BUILD
|
||||||
# actions that we will not add a /result symlink in CWD
|
# actions that we will not add a /result symlink in CWD
|
||||||
@@ -162,32 +164,33 @@ def _build_system(
|
|||||||
attr,
|
attr,
|
||||||
flake,
|
flake,
|
||||||
build_host,
|
build_host,
|
||||||
eval_flags=flake_common_flags,
|
eval_flags=grouped_nix_args.flake_common_flags,
|
||||||
flake_build_flags=flake_build_flags
|
flake_build_flags={"no_link": no_link, "dry_run": dry_run}
|
||||||
| {"no_link": no_link, "dry_run": dry_run},
|
| grouped_nix_args.flake_build_flags,
|
||||||
copy_flags=copy_flags,
|
copy_flags=grouped_nix_args.copy_flags,
|
||||||
)
|
)
|
||||||
case (None, Flake(_)):
|
case (None, Flake(_)):
|
||||||
path_to_config = nix.build_flake(
|
path_to_config = nix.build_flake(
|
||||||
attr,
|
attr,
|
||||||
flake,
|
flake,
|
||||||
flake_build_flags=flake_build_flags
|
flake_build_flags={"no_link": no_link, "dry_run": dry_run}
|
||||||
| {"no_link": no_link, "dry_run": dry_run},
|
| grouped_nix_args.flake_build_flags,
|
||||||
)
|
)
|
||||||
case (Remote(_), None):
|
case (Remote(_), None):
|
||||||
path_to_config = nix.build_remote(
|
path_to_config = nix.build_remote(
|
||||||
attr,
|
attr,
|
||||||
build_attr,
|
build_attr,
|
||||||
build_host,
|
build_host,
|
||||||
realise_flags=common_flags,
|
realise_flags=grouped_nix_args.common_flags,
|
||||||
instantiate_flags=build_flags,
|
instantiate_flags=grouped_nix_args.build_flags,
|
||||||
copy_flags=copy_flags,
|
copy_flags=grouped_nix_args.copy_flags,
|
||||||
)
|
)
|
||||||
case (None, None):
|
case (None, None):
|
||||||
path_to_config = nix.build(
|
path_to_config = nix.build(
|
||||||
attr,
|
attr,
|
||||||
build_attr,
|
build_attr,
|
||||||
build_flags=build_flags | {"no_out_link": no_link, "dry_run": dry_run},
|
build_flags={"no_out_link": no_link, "dry_run": dry_run}
|
||||||
|
| grouped_nix_args.build_flags,
|
||||||
)
|
)
|
||||||
|
|
||||||
# In dry_run mode there is nothing to copy
|
# In dry_run mode there is nothing to copy
|
||||||
@@ -197,7 +200,7 @@ def _build_system(
|
|||||||
path_to_config,
|
path_to_config,
|
||||||
to_host=target_host,
|
to_host=target_host,
|
||||||
from_host=build_host,
|
from_host=build_host,
|
||||||
copy_flags=copy_flags,
|
copy_flags=grouped_nix_args.copy_flags,
|
||||||
)
|
)
|
||||||
|
|
||||||
return path_to_config
|
return path_to_config
|
||||||
@@ -211,8 +214,7 @@ def _activate_system(
|
|||||||
profile: Profile,
|
profile: Profile,
|
||||||
flake: Flake | None,
|
flake: Flake | None,
|
||||||
build_attr: BuildAttr,
|
build_attr: BuildAttr,
|
||||||
flake_common_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
common_flags: Args,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
# Print only the result to stdout to make it easier to script
|
# Print only the result to stdout to make it easier to script
|
||||||
def print_result(msg: str, result: str | Path) -> None:
|
def print_result(msg: str, result: str | Path) -> None:
|
||||||
@@ -257,13 +259,13 @@ def _activate_system(
|
|||||||
image_name = nix.get_build_image_name_flake(
|
image_name = nix.get_build_image_name_flake(
|
||||||
flake,
|
flake,
|
||||||
args.image_variant,
|
args.image_variant,
|
||||||
eval_flags=flake_common_flags,
|
eval_flags=grouped_nix_args.flake_common_flags,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
image_name = nix.get_build_image_name(
|
image_name = nix.get_build_image_name(
|
||||||
build_attr,
|
build_attr,
|
||||||
args.image_variant,
|
args.image_variant,
|
||||||
instantiate_flags=common_flags,
|
instantiate_flags=grouped_nix_args.common_flags,
|
||||||
)
|
)
|
||||||
disk_path = path_to_config / image_name
|
disk_path = path_to_config / image_name
|
||||||
print_result("Done. The disk image can be found in", disk_path)
|
print_result("Done. The disk image can be found in", disk_path)
|
||||||
@@ -277,11 +279,7 @@ def build_and_activate_system(
|
|||||||
profile: Profile,
|
profile: Profile,
|
||||||
flake: Flake | None,
|
flake: Flake | None,
|
||||||
build_attr: BuildAttr,
|
build_attr: BuildAttr,
|
||||||
build_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
common_flags: Args,
|
|
||||||
copy_flags: Args,
|
|
||||||
flake_build_flags: Args,
|
|
||||||
flake_common_flags: Args,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
logger.info("building the system configuration...")
|
logger.info("building the system configuration...")
|
||||||
attr = _get_system_attr(
|
attr = _get_system_attr(
|
||||||
@@ -289,8 +287,7 @@ def build_and_activate_system(
|
|||||||
args=args,
|
args=args,
|
||||||
flake=flake,
|
flake=flake,
|
||||||
build_attr=build_attr,
|
build_attr=build_attr,
|
||||||
common_flags=common_flags,
|
grouped_nix_args=grouped_nix_args,
|
||||||
flake_common_flags=flake_common_flags,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.rollback:
|
if args.rollback:
|
||||||
@@ -308,11 +305,7 @@ def build_and_activate_system(
|
|||||||
target_host=target_host,
|
target_host=target_host,
|
||||||
flake=flake,
|
flake=flake,
|
||||||
build_attr=build_attr,
|
build_attr=build_attr,
|
||||||
build_flags=build_flags,
|
grouped_nix_args=grouped_nix_args,
|
||||||
common_flags=common_flags,
|
|
||||||
copy_flags=copy_flags,
|
|
||||||
flake_build_flags=flake_build_flags,
|
|
||||||
flake_common_flags=flake_common_flags,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_activate_system(
|
_activate_system(
|
||||||
@@ -323,14 +316,13 @@ def build_and_activate_system(
|
|||||||
profile=profile,
|
profile=profile,
|
||||||
flake=flake,
|
flake=flake,
|
||||||
build_attr=build_attr,
|
build_attr=build_attr,
|
||||||
common_flags=common_flags,
|
grouped_nix_args=grouped_nix_args,
|
||||||
flake_common_flags=flake_common_flags,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def edit(flake: Flake | None, flake_build_flags: Args | None = None) -> None:
|
def edit(flake: Flake | None, grouped_nix_args: GroupedNixArgs) -> None:
|
||||||
if flake:
|
if flake:
|
||||||
nix.edit_flake(flake, flake_build_flags)
|
nix.edit_flake(flake, grouped_nix_args.flake_build_flags)
|
||||||
else:
|
else:
|
||||||
nix.edit()
|
nix.edit()
|
||||||
|
|
||||||
@@ -358,17 +350,16 @@ def list_generations(
|
|||||||
def repl(
|
def repl(
|
||||||
flake: Flake | None,
|
flake: Flake | None,
|
||||||
build_attr: BuildAttr,
|
build_attr: BuildAttr,
|
||||||
flake_build_flags: Args,
|
grouped_nix_args: GroupedNixArgs,
|
||||||
build_flags: Args,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
if flake:
|
if flake:
|
||||||
nix.repl_flake(flake, flake_build_flags)
|
nix.repl_flake(flake, grouped_nix_args.flake_build_flags)
|
||||||
else:
|
else:
|
||||||
nix.repl(build_attr, build_flags)
|
nix.repl(build_attr, grouped_nix_args.build_flags)
|
||||||
|
|
||||||
|
|
||||||
def write_version_suffix(build_flags: Args) -> None:
|
def write_version_suffix(grouped_nix_args: GroupedNixArgs) -> None:
|
||||||
nixpkgs_path = nix.find_file("nixpkgs", build_flags)
|
nixpkgs_path = nix.find_file("nixpkgs", grouped_nix_args.build_flags)
|
||||||
rev = nix.get_nixpkgs_rev(nixpkgs_path)
|
rev = nix.get_nixpkgs_rev(nixpkgs_path)
|
||||||
if nixpkgs_path and rev:
|
if nixpkgs_path and rev:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ def test_parse_args() -> None:
|
|||||||
assert r1.install_grub is True
|
assert r1.install_grub is True
|
||||||
assert r1.profile_name == "system"
|
assert r1.profile_name == "system"
|
||||||
assert r1.action == "switch"
|
assert r1.action == "switch"
|
||||||
# round-trip test (ensure that we have the same flags as parsed)
|
assert nr.utils.dict_to_flags(g1.common_flags) == [
|
||||||
assert nr.utils.dict_to_flags(vars(g1["common_flags"])) == [
|
|
||||||
"--option",
|
"--option",
|
||||||
"foo1",
|
"foo1",
|
||||||
"bar1",
|
"bar1",
|
||||||
@@ -74,7 +73,13 @@ def test_parse_args() -> None:
|
|||||||
"foo2",
|
"foo2",
|
||||||
"bar2",
|
"bar2",
|
||||||
]
|
]
|
||||||
assert nr.utils.dict_to_flags(vars(g1["flake_common_flags"])) == [
|
assert nr.utils.dict_to_flags(g1.flake_common_flags) == [
|
||||||
|
"--option",
|
||||||
|
"foo1",
|
||||||
|
"bar1",
|
||||||
|
"--option",
|
||||||
|
"foo2",
|
||||||
|
"bar2",
|
||||||
"--update-input",
|
"--update-input",
|
||||||
"input1",
|
"input1",
|
||||||
"--update-input",
|
"--update-input",
|
||||||
@@ -112,13 +117,15 @@ def test_parse_args() -> None:
|
|||||||
assert r2.action == "dry-build"
|
assert r2.action == "dry-build"
|
||||||
assert r2.file == "foo"
|
assert r2.file == "foo"
|
||||||
assert r2.attr == "bar"
|
assert r2.attr == "bar"
|
||||||
# round-trip test (ensure that we have the same flags as parsed)
|
assert nr.utils.dict_to_flags(g2.common_flags) == [
|
||||||
assert nr.utils.dict_to_flags(vars(g2["common_flags"])) == [
|
|
||||||
"-vvv",
|
"-vvv",
|
||||||
"--quiet",
|
"--quiet",
|
||||||
"--quiet",
|
"--quiet",
|
||||||
]
|
]
|
||||||
assert nr.utils.dict_to_flags(vars(g2["common_build_flags"])) == [
|
assert nr.utils.dict_to_flags(g2.build_flags) == [
|
||||||
|
"-vvv",
|
||||||
|
"--quiet",
|
||||||
|
"--quiet",
|
||||||
"--include",
|
"--include",
|
||||||
"include1",
|
"include1",
|
||||||
"--include",
|
"--include",
|
||||||
@@ -178,8 +185,8 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None:
|
|||||||
"<nixpkgs/nixos>",
|
"<nixpkgs/nixos>",
|
||||||
"--attr",
|
"--attr",
|
||||||
"config.system.build.toplevel",
|
"config.system.build.toplevel",
|
||||||
"-vvv",
|
|
||||||
"--no-out-link",
|
"--no-out-link",
|
||||||
|
"-vvv",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
@@ -222,6 +229,49 @@ 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:
|
||||||
|
config_path = tmp_path / "test"
|
||||||
|
config_path.touch()
|
||||||
|
|
||||||
|
def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
|
||||||
|
return CompletedProcess([], 0, str(config_path))
|
||||||
|
|
||||||
|
mock_run.side_effect = run_side_effect
|
||||||
|
|
||||||
|
nr.execute(
|
||||||
|
[
|
||||||
|
"nixos-rebuild",
|
||||||
|
"build",
|
||||||
|
"--flake",
|
||||||
|
"/path/to/config#hostname",
|
||||||
|
"--no-build-output",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_run.call_count == 1
|
||||||
|
mock_run.assert_has_calls(
|
||||||
|
[
|
||||||
|
call(
|
||||||
|
[
|
||||||
|
"nix",
|
||||||
|
"--extra-experimental-features",
|
||||||
|
"nix-command flakes",
|
||||||
|
"build",
|
||||||
|
"--print-out-paths",
|
||||||
|
'/path/to/config#nixosConfigurations."hostname".config.system.build.toplevel',
|
||||||
|
"--no-link",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
stdout=PIPE,
|
||||||
|
**DEFAULT_RUN_KWARGS,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@patch.dict(os.environ, {}, clear=True)
|
@patch.dict(os.environ, {}, clear=True)
|
||||||
@patch("subprocess.run", autospec=True)
|
@patch("subprocess.run", autospec=True)
|
||||||
def test_execute_nix_build_vm(mock_run: Mock, tmp_path: Path) -> None:
|
def test_execute_nix_build_vm(mock_run: Mock, tmp_path: Path) -> None:
|
||||||
@@ -392,11 +442,11 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None:
|
|||||||
"build",
|
"build",
|
||||||
"--print-out-paths",
|
"--print-out-paths",
|
||||||
'/path/to/config#nixosConfigurations."hostname".config.system.build.toplevel',
|
'/path/to/config#nixosConfigurations."hostname".config.system.build.toplevel',
|
||||||
|
"--no-link",
|
||||||
"-v",
|
"-v",
|
||||||
"--option",
|
"--option",
|
||||||
"narinfo-cache-negative-ttl",
|
"narinfo-cache-negative-ttl",
|
||||||
"1200",
|
"1200",
|
||||||
"--no-link",
|
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
|
|||||||
@@ -19,7 +19,14 @@ def test_reexec(mock_build: Mock, mock_execve: Mock, monkeypatch: MonkeyPatch) -
|
|||||||
args, _ = n.parse_args(argv)
|
args, _ = n.parse_args(argv)
|
||||||
mock_build.return_value = Path("/path")
|
mock_build.return_value = Path("/path")
|
||||||
|
|
||||||
s.reexec(argv, args, {"build": True}, {"flake": True})
|
grouped_nix_args = n.models.GroupedNixArgs(
|
||||||
|
build_flags={"build": True},
|
||||||
|
common_flags={"common": True},
|
||||||
|
copy_flags={"copy": True},
|
||||||
|
flake_build_flags={"flake_build": True},
|
||||||
|
flake_common_flags={"flake_common": True},
|
||||||
|
)
|
||||||
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
mock_build.assert_has_calls(
|
mock_build.assert_has_calls(
|
||||||
[
|
[
|
||||||
call(
|
call(
|
||||||
@@ -34,7 +41,7 @@ def test_reexec(mock_build: Mock, mock_execve: Mock, monkeypatch: MonkeyPatch) -
|
|||||||
|
|
||||||
mock_build.return_value = Path("/path/new")
|
mock_build.return_value = Path("/path/new")
|
||||||
|
|
||||||
s.reexec(argv, args, {}, {})
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
# exec in the new version successfully
|
# exec in the new version successfully
|
||||||
mock_execve.assert_called_once_with(
|
mock_execve.assert_called_once_with(
|
||||||
Path("/path/new/bin/nixos-rebuild-ng"),
|
Path("/path/new/bin/nixos-rebuild-ng"),
|
||||||
@@ -45,7 +52,7 @@ def test_reexec(mock_build: Mock, mock_execve: Mock, monkeypatch: MonkeyPatch) -
|
|||||||
mock_execve.reset_mock()
|
mock_execve.reset_mock()
|
||||||
mock_execve.side_effect = [OSError("BOOM"), None]
|
mock_execve.side_effect = [OSError("BOOM"), None]
|
||||||
|
|
||||||
s.reexec(argv, args, {}, {})
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
# exec in the previous version if the new version fails
|
# exec in the previous version if the new version fails
|
||||||
mock_execve.assert_any_call(
|
mock_execve.assert_any_call(
|
||||||
Path("/path/bin/nixos-rebuild-ng"),
|
Path("/path/bin/nixos-rebuild-ng"),
|
||||||
@@ -65,18 +72,25 @@ def test_reexec_flake(
|
|||||||
args, _ = n.parse_args(argv)
|
args, _ = n.parse_args(argv)
|
||||||
mock_build.return_value = Path("/path")
|
mock_build.return_value = Path("/path")
|
||||||
|
|
||||||
s.reexec(argv, args, {"build": True}, {"flake": True})
|
grouped_nix_args = n.models.GroupedNixArgs(
|
||||||
|
build_flags={"build": True},
|
||||||
|
common_flags={"common": True},
|
||||||
|
copy_flags={"copy": True},
|
||||||
|
flake_build_flags={"flake_build": True},
|
||||||
|
flake_common_flags={"flake_common": True},
|
||||||
|
)
|
||||||
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
mock_build.assert_called_once_with(
|
mock_build.assert_called_once_with(
|
||||||
s.NIXOS_REBUILD_ATTR,
|
s.NIXOS_REBUILD_ATTR,
|
||||||
n.models.Flake(ANY, ANY),
|
n.models.Flake(ANY, ANY),
|
||||||
{"flake": True, "no_link": True},
|
{"flake_build": True, "no_link": True},
|
||||||
)
|
)
|
||||||
# do not exec if there is no new version
|
# do not exec if there is no new version
|
||||||
mock_execve.assert_not_called()
|
mock_execve.assert_not_called()
|
||||||
|
|
||||||
mock_build.return_value = Path("/path/new")
|
mock_build.return_value = Path("/path/new")
|
||||||
|
|
||||||
s.reexec(argv, args, {}, {})
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
# exec in the new version successfully
|
# exec in the new version successfully
|
||||||
mock_execve.assert_called_once_with(
|
mock_execve.assert_called_once_with(
|
||||||
Path("/path/new/bin/nixos-rebuild-ng"),
|
Path("/path/new/bin/nixos-rebuild-ng"),
|
||||||
@@ -87,7 +101,7 @@ def test_reexec_flake(
|
|||||||
mock_execve.reset_mock()
|
mock_execve.reset_mock()
|
||||||
mock_execve.side_effect = [OSError("BOOM"), None]
|
mock_execve.side_effect = [OSError("BOOM"), None]
|
||||||
|
|
||||||
s.reexec(argv, args, {}, {})
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
# exec in the previous version if the new version fails
|
# exec in the previous version if the new version fails
|
||||||
mock_execve.assert_any_call(
|
mock_execve.assert_any_call(
|
||||||
Path("/path/bin/nixos-rebuild-ng"),
|
Path("/path/bin/nixos-rebuild-ng"),
|
||||||
@@ -104,6 +118,13 @@ def test_reexec_skip_if_already_reexec(mock_build: Mock, mock_execve: Mock) -> N
|
|||||||
args, _ = n.parse_args(argv)
|
args, _ = n.parse_args(argv)
|
||||||
mock_build.return_value = Path("/path")
|
mock_build.return_value = Path("/path")
|
||||||
|
|
||||||
s.reexec(argv, args, {"build": True}, {"flake": True})
|
grouped_nix_args = n.models.GroupedNixArgs(
|
||||||
|
build_flags={"build": True},
|
||||||
|
common_flags={"common": True},
|
||||||
|
copy_flags={"copy": True},
|
||||||
|
flake_build_flags={"flake_build": True},
|
||||||
|
flake_common_flags={"flake_common": True},
|
||||||
|
)
|
||||||
|
s.reexec(argv, args, grouped_nix_args)
|
||||||
mock_build.assert_not_called()
|
mock_build.assert_not_called()
|
||||||
mock_execve.assert_not_called()
|
mock_execve.assert_not_called()
|
||||||
|
|||||||
@@ -16,18 +16,18 @@
|
|||||||
|
|
||||||
rustPlatform.buildRustPackage (finalAttrs: {
|
rustPlatform.buildRustPackage (finalAttrs: {
|
||||||
pname = "ruff";
|
pname = "ruff";
|
||||||
version = "0.14.3";
|
version = "0.14.4";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "astral-sh";
|
owner = "astral-sh";
|
||||||
repo = "ruff";
|
repo = "ruff";
|
||||||
tag = finalAttrs.version;
|
tag = finalAttrs.version;
|
||||||
hash = "sha256-iYXZyB0s3rlGV3HQLN1fuAohFUm/53VLAwA3Ahj6HzM=";
|
hash = "sha256-jRH7OOT03MDomZAJM20+J4y5+xjN1ZAV27Z44O1qCEQ=";
|
||||||
};
|
};
|
||||||
|
|
||||||
cargoBuildFlags = [ "--package=ruff" ];
|
cargoBuildFlags = [ "--package=ruff" ];
|
||||||
|
|
||||||
cargoHash = "sha256-dYXFNe+nglKelgzi2Afo0AJyt53qfCAJ7reTMMfjWOI=";
|
cargoHash = "sha256-eY7QnKVrkXaNRWMaTxigNo0kf0oK9DQU4z9x4wC3Npw=";
|
||||||
|
|
||||||
nativeBuildInputs = [ installShellFiles ];
|
nativeBuildInputs = [ installShellFiles ];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user