Revert "NixOS apply script"

Reverts #344407

This has broken nixos-rebuild switch so that it no longer updates the profile, which has bad consequences including not updating the systemd-boot menu with new generations.
This commit is contained in:
Will Fancher 2024-11-01 19:54:14 -04:00
parent e8ecffc976
commit 37ee6ba681
16 changed files with 31 additions and 502 deletions

View File

@ -12,7 +12,7 @@ system has booted, you can make the selected configuration the default
for subsequent boots:
```ShellSession
# /run/current-system/bin/apply boot
# /run/current-system/bin/switch-to-configuration boot
```
Second, you can switch to the previous configuration in a running
@ -25,11 +25,11 @@ system:
This is equivalent to running:
```ShellSession
# /nix/var/nix/profiles/system-N-link/bin/apply switch
# /nix/var/nix/profiles/system-N-link/bin/switch-to-configuration switch
```
where `N` is the number of the NixOS system configuration to roll back to.
To get a list of the available configurations, run:
where `N` is the number of the NixOS system configuration. To get a
list of the available configurations, do:
```ShellSession
$ ls -l /nix/var/nix/profiles/system-*-link

View File

@ -16,6 +16,6 @@ profile:
The most notable deviation of this profile from a standard NixOS configuration
is that after building it, you cannot switch *to* the configuration anymore.
The profile sets `config.system.switch.enable = false;`, which excludes
`apply` and `switch-to-configuration`, the central scripts called by `nixos-rebuild`, from
`switch-to-configuration`, the central script called by `nixos-rebuild`, from
your system. Removing this script makes the image lighter and slightly more
secure.

View File

@ -5,8 +5,8 @@ This chapter explains some of the internals of this command to make it simpler
for new module developers to configure their units correctly and to make it
easier to understand what is happening and why for curious administrators.
`nixos-rebuild`, like many deployment solutions, calls `apply` (or for NixOS older than 24.11, `switch-to-configuration`)
which resides in a NixOS system at `$out/bin/apply`. The
`nixos-rebuild`, like many deployment solutions, calls `switch-to-configuration`
which resides in a NixOS system at `$out/bin/switch-to-configuration`. The
script is called with the action that is to be performed like `switch`, `test`,
`boot`. There is also the `dry-activate` action which does not really perform
the actions but rather prints what it would do if you called it with `test`.

View File

@ -247,7 +247,7 @@ The first steps to all these are the same:
```ShellSession
$ sudo mv -v /boot /boot.bak &&
sudo /nix/var/nix/profiles/system/bin/apply boot
sudo /nix/var/nix/profiles/system/bin/switch-to-configuration boot
```
Cross your fingers, reboot, hopefully you should get a NixOS prompt!

View File

@ -54,16 +54,6 @@
If you experience any issues, please report them.
The original Perl script is deprecated and is planned for removal in the 25.05 release. It will remain accessible until then by setting `system.switch.enableNg` to `false`.
- Built NixOS configurations now have a `$toplevel/bin/apply` script.
Unlike `switch-to-configuration`, it is capable of performing a complete `switch` operation.
If you call `switch-to-configuration` directly, you are recommended to use `apply` instead, and remove your call to `nix-env --profile /nix/var/nix/profiles/system --set $toplevel` or similar.
It will run the switch operation as a systemd unit if available, as `nixos-rebuild switch` would.
Benefits include:
- The `apply` script reduces the roundtrips required when performing a remote deployment with `nixos-rebuild switch --target-host HOST`.
- Developers and power users can now update NixOS in a single call.
- Alternative NixOS deployment methods have feature parity with `nixos-rebuild`, and NixOS can evolve all of its switching logic in one place.
- Support for mounting filesystems from block devices protected with [dm-verity](https://docs.kernel.org/admin-guide/device-mapper/verity.html)
was added through the `boot.initrd.systemd.dmVerity` option.

View File

@ -23,7 +23,7 @@ in
};
}
({ config, ... }: {
# Don't pull in apply and switch-to-configuration by default, except when specialisations or early boot shenanigans are involved.
# Don't pull in switch-to-configuration by default, except when specialisations or early boot shenanigans are involved.
# This is mostly a Hydra optimization, so we don't rebuild all the tests every time switch-to-configuration-ng changes.
key = "no-switch-to-configuration";
system.switch.enable = mkDefault (config.isSpecialisation || config.specialisation != {} || config.virtualisation.installBootLoader);

View File

@ -1,163 +0,0 @@
#!@bash@
# This is the NixOS apply script, typically located at
#
# ${config.system.build.toplevel}/bin/apply
#
# This script is responsible for managing the profile link and calling the
# appropriate scripts for its subcommands, such as switch, boot, and test.
set -euo pipefail
toplevel=@toplevel@
subcommand=
installBootloader=
specialisation=
profile=/nix/var/nix/profiles/system
log() {
echo "$@" >&2
}
die() {
log "NixOS apply error: $*"
exit 1
}
usage() {
log "NixOS apply invocation error: $*"
cat >&2 <<EOF
Usage: apply [switch|boot|test|dry-activate] [OPTIONS]
Subcommands:
switch make the configuration the boot default and activate it
boot make the configuration the boot default
test activate the configuration, but don\'t make it the boot default
dry-activate show what would be done if this configuration were activated
Options:
--install-bootloader install the bootloader
--profile PROFILE use PROFILE as the target profile (if applicable)
--specialisation NAME use the specialisation NAME
EOF
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
switch|boot|test|dry-activate)
subcommand="$1"
;;
--install-bootloader)
installBootloader=1
;;
--profile)
if [[ $# -lt 2 ]]; then
die "missing argument for --profile"
fi
profile="$2"
shift
;;
# --rollback is not an `apply` responsibility, and it should be
# implemented by the caller of `apply` instead.
--specialisation)
if [[ $# -lt 2 ]]; then
die "missing argument for --specialisation"
fi
specialisation="$2"
shift
;;
*)
if [[ -n "$subcommand" ]]; then
die "unexpected argument or flag: $1"
else
die "unexpected subcommand or flag: $1"
fi
;;
esac
shift
done
if [ -z "$subcommand" ]; then
die "no subcommand specified"
fi
}
main() {
local cmd activity
case "$subcommand" in
boot|switch)
nix-env -p "$profile" --set "$toplevel"
;;
esac
# Using systemd-run here to protect against PTY failures/network
# disconnections during rebuild.
# See: https://github.com/NixOS/nixpkgs/issues/39118
cmd=(
"systemd-run"
"-E" "LOCALE_ARCHIVE" # Will be set to new value early in switch-to-configuration script, but interpreter starts out with old value
"-E" "NIXOS_INSTALL_BOOTLOADER=$installBootloader"
"--collect"
"--no-ask-password"
"--pipe"
"--quiet"
"--same-dir"
"--service-type=exec"
"--unit=nixos-rebuild-switch-to-configuration"
"--wait"
)
# Check if we have a working systemd-run. In chroot environments we may have
# a non-working systemd, so we fallback to not using systemd-run.
if ! "${cmd[@]}" true; then
log "Skipping systemd-run to switch configuration since it is not working in target host."
cmd=(
"env"
"-i"
"LOCALE_ARCHIVE=${LOCALE_ARCHIVE:-}"
"NIXOS_INSTALL_BOOTLOADER=$installBootloader"
)
fi
if [[ -z "$specialisation" ]]; then
cmd+=("$toplevel/bin/switch-to-configuration")
else
cmd+=("$toplevel/specialisation/$specialisation/bin/switch-to-configuration")
if ! [[ -f "${cmd[-1]}" ]]; then
log "error: specialisation not found: $specialisation"
exit 1
fi
fi
if ! "${cmd[@]}" "$subcommand"; then
case "$subcommand" in
switch)
activity="switching to the new configuration"
;;
boot)
activity="switching the boot entry to the new configuration"
;;
test)
activity="switching to the new configuration (in test mode)"
;;
dry-activate)
activity="switching to the new configuration (in dry-activate mode)"
;;
*) # Should never happen
activity="running $subcommand"
;;
esac
log "warning: error(s) occurred while $activity"
exit 1
fi
}
if ! type test_run_tests &>/dev/null; then
# We're not loaded into the test.sh, so we run main.
parse_args "$@"
main
fi

View File

@ -1,51 +0,0 @@
# Run:
# nix-build -A nixosTests.apply
#
# These are not all tests. See also nixosTests.
{
lib,
stdenvNoCC,
testers,
...
}:
let
fileset = lib.fileset.unions [
./test.sh
./apply.sh
];
in
{
unitTests = stdenvNoCC.mkDerivation {
name = "nixos-apply-unit-tests";
src = lib.fileset.toSource {
root = ./.;
inherit fileset;
};
dontBuild = true;
checkPhase = ''
./test.sh
'';
installPhase = ''
touch $out
'';
};
shellcheck =
(testers.shellcheck {
src = lib.fileset.toSource {
# This makes the error messages include the full path
root = ../../../../..;
inherit fileset;
};
}).overrideAttrs
{
postUnpack = ''
for f in $(find . -type f); do
substituteInPlace $f --replace @bash@ /usr/bin/bash
done
'';
};
}

View File

@ -1,176 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2317 disable=SC2031
# False positives:
# SC2317: Unreachable code: TEST_*
# SC2031: <variable> was modified in a subshell. That change might be lost.
# We have a lot of that, and that's expected.
# This is a unit test script for the NixOS apply script.
# It can be run quickly with the following command:
#
# ./test.sh
#
# Alternatively, run the following to run all tests and checks
#
# TODO
#
set -euo pipefail
# set -x
apply="${BASH_SOURCE[0]%/*}/apply.sh"
# source_apply() {
run_parse_args() {
bash -c "source $apply;"' parse_args "$@"' -- "$@"
}
TEST_parse_args_none() {
if errout="$(run_parse_args 2>&1)"; then
test_fail "apply without arguments should fail"
elif [[ $? -ne 1 ]]; then
test_fail "apply without arguments should exit with code 1"
fi
grep -F "no subcommand specified" <<<"$errout" >/dev/null
}
TEST_parse_args_switch() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args switch;
[[ $subcommand == switch ]]
[[ $specialisation == "" ]]
[[ $profile == "" ]]
)
}
TEST_parse_args_boot() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args boot;
[[ $subcommand == boot ]]
[[ $specialisation == "" ]]
[[ $profile == "" ]]
)
}
TEST_parse_args_test() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args test;
[[ $subcommand == test ]]
[[ $specialisation == "" ]]
[[ $profile == "" ]]
)
}
TEST_parse_args_dry_activate() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args dry-activate;
[[ $subcommand == dry-activate ]]
[[ $specialisation == "" ]]
[[ $profile == "" ]]
)
}
TEST_parse_args_unknown() {
if errout="$(run_parse_args foo 2>&1)"; then
test_fail "apply with unknown subcommand should fail"
fi
grep -F "unexpected argument or flag: foo" <<<"$errout" >/dev/null
}
TEST_parse_args_switch_specialisation_no_arg() {
if errout="$(run_parse_args switch --specialisation 2>&1)"; then
test_fail "apply with --specialisation without argument should fail"
fi
grep -F "missing argument for --specialisation" <<<"$errout" >/dev/null
}
TEST_parse_args_switch_specialisation() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args switch --specialisation low-power;
[[ $subcommand == switch ]]
[[ $specialisation == low-power ]]
[[ $profile == "" ]]
)
}
TEST_parse_args_switch_profile() {
(
# shellcheck source=nixos/modules/system/activation/apply/apply.sh
source "$apply";
parse_args switch --profile /nix/var/nix/profiles/system;
[[ $subcommand == switch ]]
[[ $specialisation == "" ]]
[[ $profile == /nix/var/nix/profiles/system ]]
)
}
# Support code
test_fail() {
echo "TEST FAILURE: $*" >&2
exit 1
}
test_print_trace() {
local frame=${1:0}
local caller
# shellcheck disable=SC2207 disable=SC2086
while caller=( $(caller $frame) ); do
echo " in ${caller[1]} at ${caller[2]}:${caller[0]}"
frame=$((frame+1));
done
}
test_on_err() {
echo "ERROR running: ${BASH_COMMAND}" >&2
test_print_trace 1 >&2
}
test_init() {
trap 'test_on_err' ERR
}
test_find() {
declare -F | grep -o 'TEST_.*' | sort
}
test_run_tests() {
local status=0
for test in $(test_find); do
set +e
(
set -eEuo pipefail
trap 'test_on_err' ERR
$test
)
r=$?
set -e
if [[ $r == 0 ]]; then
echo "ok: $test"
else
echo "TEST FAIL: $test"; status=1;
fi
done
if [[ $status == 0 ]]; then
echo "All good"
else
echo
echo "TEST SUITE FAILED"
fi
exit $status
}
# Main
test_init
test_run_tests

View File

@ -42,7 +42,7 @@ in
(e.g. `fewJobsManyCores`) at runtime, run:
```
sudo /run/current-system/specialisation/fewJobsManyCores/bin/apply test
sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
```
'';
type = types.attrsOf (types.submodule (

View File

@ -80,9 +80,12 @@ if ("@localeArchive@" ne "") {
if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) {
print STDERR <<"EOF";
error: Unknown action $action
Usage: $0 [switch|boot|test|dry-activate]
Consider calling `apply` instead of `switch-to-configuration`.
switch: make the configuration the boot default and activate now
boot: make the configuration the boot default
test: activate the configuration, but don\'t make it the boot default
dry-activate: show what would be done if this configuration were activated
EOF
exit(1);
}

View File

@ -40,30 +40,7 @@ in
};
};
options.system.apply.enable = lib.mkOption {
type = lib.types.bool;
default = config.system.switch.enable;
internal = true;
description = ''
Whether to include the `bin/apply` script.
Disabling puts `nixos-rebuild` in a legacy mode that won't be maintained
and removes cheap and useful functionality. It's also slower over ssh.
This should only be used for testing the `nixos-rebuild` command, to
pretend that the configuration is an old NixOS.
'';
};
config = lib.mkMerge [
(lib.mkIf config.system.apply.enable {
system.activatableSystemBuilderCommands = ''
mkdir -p $out/bin
substitute ${./apply/apply.sh} $out/bin/apply \
--subst-var-by bash ${lib.getExe pkgs.bash} \
--subst-var-by toplevel ''${!toplevelVar}
chmod +x $out/bin/apply
'';
})
(lib.mkIf (config.system.switch.enable && !config.system.switch.enableNg) {
warnings = [
''
@ -77,7 +54,7 @@ in
];
system.activatableSystemBuilderCommands = ''
mkdir -p $out/bin
mkdir $out/bin
substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
--subst-var out \
--subst-var-by toplevel ''${!toplevelVar} \
@ -109,7 +86,7 @@ in
(
source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook
mkdir -p $out/bin
mkdir $out/bin
ln -sf ${lib.getExe pkgs.switch-to-configuration-ng} $out/bin/switch-to-configuration
wrapProgram $out/bin/switch-to-configuration \
--set OUT $out \

View File

@ -49,8 +49,8 @@ let
# Putting it all together. This builds a store path containing
# symlinks to the various parts of the built configuration (the
# kernel, systemd units, init scripts, etc.) as well as a script
# `bin/apply` that activates the configuration and
# makes it bootable. See `activatable-system.nix` and `switchable-system.nix`.
# `switch-to-configuration' that activates the configuration and
# makes it bootable. See `activatable-system.nix`.
baseSystem = pkgs.stdenvNoCC.mkDerivation ({
name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
preferLocalBuild = true;

View File

@ -129,7 +129,6 @@ in {
apfs = runTest ./apfs.nix;
appliance-repart-image = runTest ./appliance-repart-image.nix;
appliance-repart-image-verity-store = runTest ./appliance-repart-image-verity-store.nix;
apply = pkgs.callPackage ../modules/system/activation/apply/checks.nix { };
apparmor = handleTest ./apparmor.nix {};
archi = handleTest ./archi.nix {};
aria2 = handleTest ./aria2.nix {};
@ -704,17 +703,7 @@ in {
nixos-generate-config = handleTest ./nixos-generate-config.nix {};
nixos-rebuild-install-bootloader = handleTestOn ["x86_64-linux"] ./nixos-rebuild-install-bootloader.nix {};
nixos-rebuild-specialisations = runTestOn ["x86_64-linux"] ./nixos-rebuild-specialisations.nix;
nixos-rebuild-specialisations-legacy = runTestOn ["x86_64-linux"] {
name = mkForce "nixos-rebuild-specialisations-legacy";
imports = [ ./nixos-rebuild-specialisations.nix ];
extraBaseModules = { system.apply.enable = false; };
};
nixos-rebuild-target-host = runTest ./nixos-rebuild-target-host.nix;
nixos-rebuild-target-host-legacy = runTest {
name = mkForce "nixos-rebuild-target-host-legacy";
imports = [ ./nixos-rebuild-target-host.nix ];
extraBaseModules = { system.apply.enable = false; };
};
nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
nixseparatedebuginfod = handleTest ./nixseparatedebuginfod.nix {};
node-red = handleTest ./node-red.nix {};

View File

@ -940,7 +940,10 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> {
fn usage(argv0: &str) -> ! {
eprintln!(
r#"Usage: {} [switch|boot|test|dry-activate]
Consider calling `apply` instead of `switch-to-configuration`.
switch: make the configuration the boot default and activate now
boot: make the configuration the boot default
test: activate the configuration, but don't make it the boot default
dry-activate: show what would be done if this configuration were activated
"#,
argv0
);

View File

@ -220,9 +220,9 @@ buildHostCmd() {
if [ -z "$buildHost" ]; then
runCmd "$@"
elif [ -n "$remoteNix" ]; then
runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" env PATH="$remoteNix":'$PATH' "${@@Q}"
runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" env PATH="$remoteNix":'$PATH' "$@"
else
runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" "${@@Q}"
runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" "$@"
fi
}
@ -237,7 +237,7 @@ targetHostCmd() {
if [ -z "$targetHost" ]; then
runCmd "${c[@]}" "$@"
else
runCmd ssh $SSHOPTS "$targetHost" "${c[@]}" "${@@Q}"
runCmd ssh $SSHOPTS "$targetHost" "${c[@]}" "$@"
fi
}
@ -790,6 +790,7 @@ if [ -z "$rollback" ]; then
pathToConfig="$(nixFlakeBuild "$flake#$flakeAttr.config.system.build.toplevel" "${extraBuildFlags[@]}" "${lockFlags[@]}")"
fi
copyToTarget "$pathToConfig"
targetHostSudoCmd nix-env -p "$profile" --set "$pathToConfig"
elif [[ "$action" = test || "$action" = build || "$action" = dry-build || "$action" = dry-activate ]]; then
if [[ -z $buildingAttribute ]]; then
pathToConfig="$(nixBuild $buildFile -A "${attr:+$attr.}config.system.build.toplevel" "${extraBuildFlags[@]}")"
@ -840,56 +841,12 @@ else # [ -n "$rollback" ]
fi
hasApplyScript=
# If we're doing a deployment-like action, we need to know whether the config has
# an apply script. NixOS versions >= 24.11 should be deployed with toplevel/bin/apply.
if [[ "$action" = switch || "$action" = boot || "$action" = test || "$action" = dry-activate ]]; then
hasApplyScriptOut="$(targetHostCmd sh -c "if test -e $pathToConfig/bin/apply; then echo __has-apply-script__; elif test -e $pathToConfig/bin; then echo __has-no-apply-script__; else echo $pathToConfig is gone; fi
")"
# SSH can be messy (e.g. when user has a shell rc file that prints to stdout)
# So we only check for the substring
case "$hasApplyScriptOut" in
*__has-apply-script__*)
hasApplyScript=1
;;
*__has-no-apply-script__*)
hasApplyScript=
;;
*)
# Unlikely
echo "$hasApplyScriptOut" 1>&2
log "error: $pathToConfig could not be read"
exit 1
;;
esac
fi
# switch|boot|test|dry-activate
#
# If we're not just building, then make the new configuration the boot
# default and/or activate it now.
if [[ -n "$hasApplyScript" ]] \
&& [[ "$action" = switch || "$action" = boot || "$action" = test || "$action" = dry-activate ]]; then
cmd=("$pathToConfig/bin/apply" "$action" "--profile" "$profile")
if [[ -n "$specialisation" ]]; then
cmd+=("--specialisation" "$specialisation")
fi
if [[ -n "$installBootloader" ]]; then
cmd+=("--install-bootloader")
fi
targetHostSudoCmd "${cmd[@]}"
elif [[ "$action" = switch || "$action" = boot || "$action" = test || "$action" = dry-activate ]]; then
# Legacy, without apply script, NixOS < 24.11
if [[ "$action" = switch || "$action" = boot ]]; then
if [[ -z "$rollback" ]]; then
: # We've already switched it so that hasApplyScript would check the right $pathToConfig
else
targetHostSudoCmd nix-env -p "$profile" --set "$pathToConfig"
fi
fi
# Legacy logic to support deploying NixOS <24.11; see hasApplyScript
if [[ "$action" = switch || "$action" = boot || "$action" = test || "$action" = dry-activate ]]; then
# Using systemd-run here to protect against PTY failures/network
# disconnections during rebuild.
# See: https://github.com/NixOS/nixpkgs/issues/39118
cmd=(
"systemd-run"
"-E" "LOCALE_ARCHIVE" # Will be set to new value early in switch-to-configuration script, but interpreter starts out with old value