mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-22 15:03:28 +00:00
nixos/toplevel: Add bin/apply
This commit is contained in:
parent
2fcea20ca0
commit
6427500989
146
nixos/modules/system/activation/apply/apply.sh
Normal file
146
nixos/modules/system/activation/apply/apply.sh
Normal file
@ -0,0 +1,146 @@
|
||||
#!@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
|
||||
}
|
||||
|
||||
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
|
51
nixos/modules/system/activation/apply/checks.nix
Normal file
51
nixos/modules/system/activation/apply/checks.nix
Normal file
@ -0,0 +1,51 @@
|
||||
# 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
|
||||
'';
|
||||
};
|
||||
}
|
176
nixos/modules/system/activation/apply/test.sh
Executable file
176
nixos/modules/system/activation/apply/test.sh
Executable file
@ -0,0 +1,176 @@
|
||||
#!/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
|
@ -40,7 +40,30 @@ 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 = [
|
||||
''
|
||||
@ -54,7 +77,7 @@ in
|
||||
];
|
||||
|
||||
system.activatableSystemBuilderCommands = ''
|
||||
mkdir $out/bin
|
||||
mkdir -p $out/bin
|
||||
substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
|
||||
--subst-var out \
|
||||
--subst-var-by toplevel ''${!toplevelVar} \
|
||||
@ -86,7 +109,7 @@ in
|
||||
(
|
||||
source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook
|
||||
|
||||
mkdir $out/bin
|
||||
mkdir -p $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 \
|
||||
|
@ -129,6 +129,7 @@ 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 {};
|
||||
|
Loading…
Reference in New Issue
Block a user