diff options
Diffstat (limited to 'pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh')
-rwxr-xr-x | pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh | 383 |
1 files changed, 285 insertions, 98 deletions
diff --git a/pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh b/pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh index b0363fa7a7c..dddae8da206 100755 --- a/pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh +++ b/pkgs/os-specific/linux/nixos-rebuild/nixos-rebuild.sh @@ -1,4 +1,5 @@ #! @runtimeShell@ +# shellcheck shell=bash if [ -x "@runtimeShell@" ]; then export SHELL="@runtimeShell@"; fi; @@ -16,9 +17,10 @@ showSyntax() { # Parse the command line. origArgs=("$@") +copyFlags=() extraBuildFlags=() lockFlags=() -flakeFlags=() +flakeFlags=(--extra-experimental-features 'nix-command flakes') action= buildNix=1 fast= @@ -26,9 +28,20 @@ rollback= upgrade= upgrade_all= profile=/nix/var/nix/profiles/system +specialisation= buildHost= targetHost= -maybeSudo=() +remoteSudo= +verboseScript= +noFlake= +# comma separated list of vars to preserve when using sudo +preservedSudoVars=NIXOS_INSTALL_BOOTLOADER +json= + +# log the given argument to stderr +log() { + echo "$@" >&2 +} while [ "$#" -gt 0 ]; do i="$1"; shift 1 @@ -36,12 +49,14 @@ while [ "$#" -gt 0 ]; do --help) showSyntax ;; - switch|boot|test|build|edit|dry-build|dry-run|dry-activate|build-vm|build-vm-with-bootloader) + switch|boot|test|build|edit|dry-build|dry-run|dry-activate|build-vm|build-vm-with-bootloader|list-generations) if [ "$i" = dry-run ]; then i=dry-build; fi + # exactly one action mandatory, bail out if multiple are given + if [ -n "$action" ]; then showSyntax; fi action="$i" ;; --install-grub) - echo "$0: --install-grub deprecated, use --install-bootloader instead" >&2 + log "$0: --install-grub deprecated, use --install-bootloader instead" export NIXOS_INSTALL_BOOTLOADER=1 ;; --install-bootloader) @@ -60,11 +75,18 @@ while [ "$#" -gt 0 ]; do upgrade=1 upgrade_all=1 ;; - --max-jobs|-j|--cores|-I|--builders) + --use-substitutes|--substitute-on-destination|-s) + copyFlags+=("-s") + ;; + -I|--max-jobs|-j|--cores|--builders|--log-format) j="$1"; shift 1 extraBuildFlags+=("$i" "$j") ;; - --show-trace|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q|-j*|-L|--refresh|--no-net|--offline|--impure) + -j*|--quiet|--print-build-logs|-L|--no-build-output|-Q| --show-trace|--keep-going|-k|--keep-failed|-K|--fallback|--refresh|--repair|--impure|--offline|--no-net) + extraBuildFlags+=("$i") + ;; + --verbose|-v|-vv|-vvv|-vvvv|-vvvvv) + verboseScript="true" extraBuildFlags+=("$i") ;; --option) @@ -78,7 +100,7 @@ while [ "$#" -gt 0 ]; do ;; --profile-name|-p) if [ -z "$1" ]; then - echo "$0: ‘--profile-name’ requires an argument" + log "$0: ‘--profile-name’ requires an argument" exit 1 fi if [ "$1" != system ]; then @@ -87,22 +109,32 @@ while [ "$#" -gt 0 ]; do fi shift 1 ;; - --build-host|h) + --specialisation|-c) + if [ -z "$1" ]; then + log "$0: ‘--specialisation’ requires an argument" + exit 1 + fi + specialisation="$1" + shift 1 + ;; + --build-host) buildHost="$1" shift 1 ;; - --target-host|t) + --target-host) targetHost="$1" shift 1 ;; --use-remote-sudo) - maybeSudo=(sudo --) + remoteSudo=1 ;; --flake) flake="$1" - flakeFlags=(--extra-experimental-features 'nix-command flakes') shift 1 ;; + --no-flake) + noFlake=1 + ;; --recreate-lock-file|--no-update-lock-file|--no-write-lock-file|--no-registries|--commit-lock-file) lockFlags+=("$i") ;; @@ -115,61 +147,72 @@ while [ "$#" -gt 0 ]; do k="$1"; shift 1 lockFlags+=("$i" "$j" "$k") ;; + --json) + json=1 + ;; *) - echo "$0: unknown option \`$i'" + log "$0: unknown option \`$i'" exit 1 ;; esac done -if [ -n "$SUDO_USER" ]; then - maybeSudo=(sudo --) +if [[ -n "$SUDO_USER" || -n $remoteSudo ]]; then + maybeSudo=(sudo --preserve-env="$preservedSudoVars" --) fi -if [ -z "$buildHost" -a -n "$targetHost" ]; then - buildHost="$targetHost" -fi -if [ "$targetHost" = localhost ]; then - targetHost= -fi -if [ "$buildHost" = localhost ]; then - buildHost= -fi +# log the given argument to stderr if verbose mode is on +logVerbose() { + if [ -n "$verboseScript" ]; then + echo "$@" >&2 + fi +} + +# Run a command, logging it first if verbose mode is on +runCmd() { + logVerbose "$" "$@" + "$@" +} buildHostCmd() { if [ -z "$buildHost" ]; then - "$@" + runCmd "$@" elif [ -n "$remoteNix" ]; then - ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" env PATH="$remoteNix":'$PATH' "$@" + runCmd ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" env PATH="$remoteNix":'$PATH' "$@" else - ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" "$@" + runCmd ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" "$@" fi } targetHostCmd() { if [ -z "$targetHost" ]; then - "${maybeSudo[@]}" "$@" + runCmd "${maybeSudo[@]}" "$@" else - ssh $SSHOPTS "$targetHost" "${maybeSudo[@]}" "$@" + runCmd ssh $SSHOPTS "$targetHost" "${maybeSudo[@]}" "$@" fi } copyToTarget() { if ! [ "$targetHost" = "$buildHost" ]; then if [ -z "$targetHost" ]; then - NIX_SSHOPTS=$SSHOPTS nix-copy-closure --from "$buildHost" "$1" + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure "${copyFlags[@]}" --from "$buildHost" "$1" elif [ -z "$buildHost" ]; then - NIX_SSHOPTS=$SSHOPTS nix-copy-closure --to "$targetHost" "$1" + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure "${copyFlags[@]}" --to "$targetHost" "$1" else - buildHostCmd nix-copy-closure --to "$targetHost" "$1" + buildHostCmd nix-copy-closure "${copyFlags[@]}" --to "$targetHost" "$1" fi fi } nixBuild() { + logVerbose "Building in legacy (non-flake) mode." if [ -z "$buildHost" ]; then - nix-build "$@" + logVerbose "No --build-host given, running nix-build locally" + runCmd nix-build "$@" else + logVerbose "buildHost set to \"$buildHost\", running nix-build remotely" local instArgs=() local buildArgs=() local drv= @@ -199,25 +242,26 @@ nixBuild() { esac done - drv="$(nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")" + drv="$(runCmd nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")" if [ -a "$drv" ]; then - NIX_SSHOPTS=$SSHOPTS nix-copy-closure --to "$buildHost" "$drv" + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure --to "$buildHost" "$drv" buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" else - echo "nix-instantiate failed" + log "nix-instantiate failed" exit 1 fi fi } nixFlakeBuild() { - if [[ -z "$buildHost" && -z "$targetHost" ]] && - ! [ "$action" = switch -o "$action" = boot ] + logVerbose "Building in flake mode." + if [[ -z "$buildHost" && -z "$targetHost" && "$action" != switch && "$action" != boot && "$action" != test && "$action" != dry-activate ]] then - nix "${flakeFlags[@]}" build "$@" + runCmd nix "${flakeFlags[@]}" build "$@" readlink -f ./result elif [ -z "$buildHost" ]; then - nix "${flakeFlags[@]}" build "$@" --out-link "${tmpDir}/result" + runCmd nix "${flakeFlags[@]}" build "$@" --out-link "${tmpDir}/result" readlink -f "${tmpDir}/result" else local attr="$1" @@ -241,18 +285,21 @@ nixFlakeBuild() { local k="$1"; shift 1 evalArgs+=("$i" "$j" "$k") ;; + --impure) # We don't want this in buildArgs, it's only needed at evaluation time, and unsupported during realisation + ;; *) buildArgs+=("$i") ;; esac done - drv="$(nix "${flakeFlags[@]}" eval --raw "${attr}.drvPath" "${evalArgs[@]}" "${extraBuildFlags[@]}")" + drv="$(runCmd nix "${flakeFlags[@]}" eval --raw "${attr}.drvPath" "${evalArgs[@]}" "${extraBuildFlags[@]}")" if [ -a "$drv" ]; then - NIX_SSHOPTS=$SSHOPTS nix "${flakeFlags[@]}" copy --derivation --to "ssh://$buildHost" "$drv" + logVerbose "Running nix with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix "${flakeFlags[@]}" copy "${copyFlags[@]}" --derivation --to "ssh://$buildHost" "$drv" buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" else - echo "nix eval failed" + log "nix eval failed" exit 1 fi fi @@ -267,7 +314,7 @@ if [ -z "$action" ]; then showSyntax; fi # executed, so it's safe to run nixos-rebuild against a potentially # untrusted tree. canRun= -if [ "$action" = switch -o "$action" = boot -o "$action" = test ]; then +if [[ "$action" = switch || "$action" = boot || "$action" = test ]]; then canRun=1 fi @@ -283,11 +330,11 @@ if [[ -n $upgrade && -z $_NIXOS_REBUILD_REEXEC && -z $flake ]]; then channel_name=$(basename "$channelpath") if [[ "$channel_name" == "nixos" ]]; then - nix-channel --update "$channel_name" + runCmd nix-channel --update "$channel_name" elif [ -e "$channelpath/.update-on-nixos-rebuild" ]; then - nix-channel --update "$channel_name" + runCmd nix-channel --update "$channel_name" elif [[ -n $upgrade_all ]] ; then - nix-channel --update "$channel_name" + runCmd nix-channel --update "$channel_name" fi done fi @@ -305,20 +352,10 @@ fi # Use /etc/nixos/flake.nix if it exists. It can be a symlink to the # actual flake. -if [[ -z $flake && -e /etc/nixos/flake.nix ]]; then +if [[ -z $flake && -e /etc/nixos/flake.nix && -z $noFlake ]]; then flake="$(dirname "$(readlink -f /etc/nixos/flake.nix)")" fi -# Re-execute nixos-rebuild from the Nixpkgs tree. -# FIXME: get nixos-rebuild from $flake. -if [[ -z $_NIXOS_REBUILD_REEXEC && -n $canRun && -z $fast && -z $flake ]]; then - if p=$(nix-build --no-out-link --expr 'with import <nixpkgs/nixos> {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then - export _NIXOS_REBUILD_REEXEC=1 - exec "$p/bin/nixos-rebuild" "${origArgs[@]}" - exit 1 - fi -fi - # For convenience, use the hostname as the default configuration to # build from the flake. if [[ -n $flake ]]; then @@ -337,41 +374,63 @@ if [[ -n $flake ]]; then fi fi -# Resolve the flake. -if [[ -n $flake ]]; then - flake=$(nix "${flakeFlags[@]}" flake metadata --json "${extraBuildFlags[@]}" "${lockFlags[@]}" -- "$flake" | jq -r .url) +if [[ ! -z "$specialisation" && ! "$action" = switch && ! "$action" = test ]]; then + log "error: ‘--specialisation’ can only be used with ‘switch’ and ‘test’" + exit 1 +fi + +tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX) + +cleanup() { + for ctrl in "$tmpDir"/ssh-*; do + ssh -o ControlPath="$ctrl" -O exit dummyhost 2>/dev/null || true + done + rm -rf "$tmpDir" +} +trap cleanup EXIT + + +# Re-execute nixos-rebuild from the Nixpkgs tree. +if [[ -z $_NIXOS_REBUILD_REEXEC && -n $canRun && -z $fast ]]; then + if [[ -z $flake ]]; then + if p=$(runCmd nix-build --no-out-link --expr 'with import <nixpkgs/nixos> {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then + SHOULD_REEXEC=1 + fi + else + runCmd nix "${flakeFlags[@]}" build --out-link "${tmpDir}/nixos-rebuild" "$flake#$flakeAttr.config.system.build.nixos-rebuild" "${extraBuildFlags[@]}" "${lockFlags[@]}" + if p=$(readlink -e "${tmpDir}/nixos-rebuild"); then + SHOULD_REEXEC=1 + fi + fi + + if [[ -n $SHOULD_REEXEC ]]; then + export _NIXOS_REBUILD_REEXEC=1 + # Manually call cleanup as the EXIT trap is not triggered when using exec + cleanup + runCmd exec "$p/bin/nixos-rebuild" "${origArgs[@]}" + exit 1 + fi fi # Find configuration.nix and open editor instead of building. if [ "$action" = edit ]; then if [[ -z $flake ]]; then - NIXOS_CONFIG=${NIXOS_CONFIG:-$(nix-instantiate --find-file nixos-config)} + NIXOS_CONFIG=${NIXOS_CONFIG:-$(runCmd nix-instantiate --find-file nixos-config)} if [[ -d $NIXOS_CONFIG ]]; then NIXOS_CONFIG=$NIXOS_CONFIG/default.nix fi - exec ${EDITOR:-nano} "$NIXOS_CONFIG" + runCmd exec ${EDITOR:-nano} "$NIXOS_CONFIG" else - exec nix "${flakeFlags[@]}" edit "${lockFlags[@]}" -- "$flake#$flakeAttr" + runCmd exec nix "${flakeFlags[@]}" edit "${lockFlags[@]}" -- "$flake#$flakeAttr" fi exit 1 fi - -tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX) SSHOPTS="$NIX_SSHOPTS -o ControlMaster=auto -o ControlPath=$tmpDir/ssh-%n -o ControlPersist=60" -cleanup() { - for ctrl in "$tmpDir"/ssh-*; do - ssh -o ControlPath="$ctrl" -O exit dummyhost 2>/dev/null || true - done - rm -rf "$tmpDir" -} -trap cleanup EXIT - - # First build Nix, since NixOS may require a newer version than the # current one. -if [ -n "$rollback" -o "$action" = dry-build ]; then +if [[ -n "$rollback" || "$action" = dry-build ]]; then buildNix= fi @@ -392,32 +451,32 @@ prebuiltNix() { elif [[ "$machine" = aarch64 ]]; then echo @nix_aarch64_linux@ else - echo "$0: unsupported platform" + log "$0: unsupported platform" exit 1 fi } if [[ -n $buildNix && -z $flake ]]; then - echo "building Nix..." >&2 + log "building Nix..." nixDrv= - if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root "$tmpDir/nix.drv" --indirect -A config.nix.package.out "${extraBuildFlags[@]}")"; then - if ! nixDrv="$(nix-instantiate '<nixpkgs>' --add-root "$tmpDir/nix.drv" --indirect -A nix "${extraBuildFlags[@]}")"; then - if ! nixStorePath="$(nix-instantiate --eval '<nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix>' -A "$(nixSystem)" | sed -e 's/^"//' -e 's/"$//')"; then + if ! nixDrv="$(runCmd nix-instantiate '<nixpkgs/nixos>' --add-root "$tmpDir/nix.drv" --indirect -A config.nix.package.out "${extraBuildFlags[@]}")"; then + if ! nixDrv="$(runCmd nix-instantiate '<nixpkgs>' --add-root "$tmpDir/nix.drv" --indirect -A nix "${extraBuildFlags[@]}")"; then + if ! nixStorePath="$(runCmd nix-instantiate --eval '<nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix>' -A "$(nixSystem)" | sed -e 's/^"//' -e 's/"$//')"; then nixStorePath="$(prebuiltNix "$(uname -m)")" fi - if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \ + if ! runCmd nix-store -r "$nixStorePath" --add-root "${tmpDir}/nix" --indirect \ --option extra-binary-caches https://cache.nixos.org/; then - echo "warning: don't know how to get latest Nix" >&2 + log "warning: don't know how to get latest Nix" fi # Older version of nix-store -r don't support --add-root. [ -e "$tmpDir/nix" ] || ln -sf "$nixStorePath" "$tmpDir/nix" if [ -n "$buildHost" ]; then - remoteNixStorePath="$(prebuiltNix "$(buildHostCmd uname -m)")" + remoteNixStorePath="$(runCmd prebuiltNix "$(buildHostCmd uname -m)")" remoteNix="$remoteNixStorePath/bin" if ! buildHostCmd nix-store -r "$remoteNixStorePath" \ --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then remoteNix= - echo "warning: don't know how to get latest Nix" >&2 + log "warning: don't know how to get latest Nix" fi fi fi @@ -425,7 +484,7 @@ if [[ -n $buildNix && -z $flake ]]; then if [ -a "$nixDrv" ]; then nix-store -r "$nixDrv"'!'"out" --add-root "$tmpDir/nix" --indirect >/dev/null if [ -n "$buildHost" ]; then - nix-copy-closure --to "$buildHost" "$nixDrv" + nix-copy-closure "${copyFlags[@]}" --to "$buildHost" "$nixDrv" # The nix build produces multiple outputs, we add them all to the remote path for p in $(buildHostCmd nix-store -r "$(readlink "$nixDrv")" "${buildArgs[@]}"); do remoteNix="$remoteNix${remoteNix:+:}$p/bin" @@ -439,8 +498,8 @@ fi # Update the version suffix if we're building from Git (so that # nixos-version shows something useful). if [[ -n $canRun && -z $flake ]]; then - if nixpkgs=$(nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then - suffix=$($SHELL "$nixpkgs/nixos/modules/installer/tools/get-version-suffix" "${extraBuildFlags[@]}" || true) + if nixpkgs=$(runCmd nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then + suffix=$(runCmd $SHELL "$nixpkgs/nixos/modules/installer/tools/get-version-suffix" "${extraBuildFlags[@]}" || true) if [ -n "$suffix" ]; then echo -n "$suffix" > "$nixpkgs/.version-suffix" || true fi @@ -452,13 +511,94 @@ if [ "$action" = dry-build ]; then extraBuildFlags+=(--dry-run) fi +if [ "$action" = list-generations ]; then + if [ ! -L "$profile" ]; then + log "No profile \`$(basename "$profile")' found" + exit 1 + fi + + generation_from_dir() { + generation_dir="$1" + generation_base="$(basename "$generation_dir")" # Has the format "system-123-link" for generation 123 + no_link_gen="${generation_base%-link}" # remove the "-link" + echo "${no_link_gen##*-}" # remove everything before the last dash + } + describe_generation(){ + generation_dir="$1" + generation_number="$(generation_from_dir "$generation_dir")" + nixos_version="$(cat "$generation_dir/nixos-version" 2> /dev/null || echo "Unknown")" + + kernel_dir="$(dirname "$(realpath "$generation_dir/kernel")")" + kernel_version="$(ls "$kernel_dir/lib/modules" || echo "Unknown")" + + configurationRevision="$("$generation_dir/sw/bin/nixos-version" --configuration-revision 2> /dev/null || true)" + + # Old nixos-version output ignored unknown flags and just printed the version + # therefore the following workaround is done not to show the default output + nixos_version_default="$("$generation_dir/sw/bin/nixos-version")" + if [ "$configurationRevision" == "$nixos_version_default" ]; then + configurationRevision="" + fi + + # jq automatically quotes the output => don't try to quote it in output! + build_date="$(stat "$generation_dir" --format=%W | jq 'todate')" + + pushd "$generation_dir/specialisation/" > /dev/null || : + specialisation_list=(*) + popd > /dev/null || : + + specialisations="$(jq --compact-output --null-input '$ARGS.positional' --args -- "${specialisation_list[@]}")" + + if [ "$(basename "$generation_dir")" = "$(readlink "$profile")" ]; then + current_generation_tag="true" + else + current_generation_tag="false" + fi + + # Escape userdefined strings + nixos_version="$(jq -aR <<< "$nixos_version")" + kernel_version="$(jq -aR <<< "$kernel_version")" + configurationRevision="$(jq -aR <<< "$configurationRevision")" + cat << EOF +{ + "generation": $generation_number, + "date": $build_date, + "nixosVersion": $nixos_version, + "kernelVersion": $kernel_version, + "configurationRevision": $configurationRevision, + "specialisations": $specialisations, + "current": $current_generation_tag +} +EOF + } + + find "$(dirname "$profile")" -regex "$profile-[0-9]+-link" | + sort -Vr | + while read -r generation_dir; do + describe_generation "$generation_dir" + done | + if [ -z "$json" ]; then + jq --slurp -r '.[] | [ + ([.generation, (if .current == true then "current" else "" end)] | join(" ")), + (.date | fromdate | strflocaltime("%Y-%m-%d %H:%M:%S")), + .nixosVersion, .kernelVersion, .configurationRevision, + (.specialisations | join(" ")) + ] | @tsv' | + column --separator $'\t' --table --table-columns "Generation,Build-date,NixOS version,Kernel,Configuration Revision,Specialisation" | + ${PAGER:cat} + else + jq --slurp . + fi + exit 0 +fi + # Either upgrade the configuration in the system profile (for "switch" # or "boot"), or just build it and create a symlink "result" in the # current directory (for "build" and "test"). if [ -z "$rollback" ]; then - echo "building the system configuration..." >&2 - if [ "$action" = switch -o "$action" = boot ]; then + log "building the system configuration..." + if [[ "$action" = switch || "$action" = boot ]]; then if [[ -z $flake ]]; then pathToConfig="$(nixBuild '<nixpkgs/nixos>' --no-out-link -A system "${extraBuildFlags[@]}")" else @@ -466,7 +606,7 @@ if [ -z "$rollback" ]; then fi copyToTarget "$pathToConfig" targetHostCmd nix-env -p "$profile" --set "$pathToConfig" - elif [ "$action" = test -o "$action" = build -o "$action" = dry-build -o "$action" = dry-activate ]; then + elif [[ "$action" = test || "$action" = build || "$action" = dry-build || "$action" = dry-activate ]]; then if [[ -z $flake ]]; then pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A system -k "${extraBuildFlags[@]}")" else @@ -488,14 +628,14 @@ if [ -z "$rollback" ]; then showSyntax fi # Copy build to target host if we haven't already done it - if ! [ "$action" = switch -o "$action" = boot ]; then + if ! [[ "$action" = switch || "$action" = boot ]]; then copyToTarget "$pathToConfig" fi else # [ -n "$rollback" ] - if [ "$action" = switch -o "$action" = boot ]; then + if [[ "$action" = switch || "$action" = boot ]]; then targetHostCmd nix-env --rollback -p "$profile" pathToConfig="$profile" - elif [ "$action" = test -o "$action" = build ]; then + elif [[ "$action" = test || "$action" = build ]]; then systemNumber=$( targetHostCmd nix-env -p "$profile" --list-generations | sed -n '/current/ {g; p;}; s/ *\([0-9]*\).*/\1/; h' @@ -512,17 +652,64 @@ fi # If we're not just building, then make the new configuration the boot # default and/or activate it now. -if [ "$action" = switch -o "$action" = boot -o "$action" = test -o "$action" = dry-activate ]; then - if ! targetHostCmd "$pathToConfig/bin/switch-to-configuration" "$action"; then - echo "warning: error(s) occurred while switching to the new configuration" >&2 +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 + "-E" "NIXOS_INSTALL_BOOTLOADER" + "--collect" + "--no-ask-password" + "--pty" + "--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. + # You may also want to explicitly set NIXOS_SWITCH_USE_DIRTY_ENV environment + # variable, since systemd-run runs inside an isolated environment and + # this may break some post-switch scripts. However keep in mind that this + # may be dangerous in remote access (e.g. SSH). + if [[ -n "$NIXOS_SWITCH_USE_DIRTY_ENV" ]]; then + log "warning: skipping systemd-run since NIXOS_SWITCH_USE_DIRTY_ENV is set. This environment variable will be ignored in the future" + cmd=() + elif ! targetHostCmd "${cmd[@]}" true &>/dev/null; then + logVerbose "Skipping systemd-run to switch configuration since it is not working in target host." + cmd=( + "env" + "-i" + "LOCALE_ARCHIVE=$LOCALE_ARCHIVE" + "NIXOS_INSTALL_BOOTLOADER=$NIXOS_INSTALL_BOOTLOADER" + ) + else + logVerbose "Using systemd-run to switch configuration." + fi + if [[ -z "$specialisation" ]]; then + cmd+=("$pathToConfig/bin/switch-to-configuration") + else + cmd+=("$pathToConfig/specialisation/$specialisation/bin/switch-to-configuration") + + if [[ ! -f "${cmd[-1]}" ]]; then + log "error: specialisation not found: $specialisation" + exit 1 + fi + fi + + if ! targetHostCmd "${cmd[@]}" "$action"; then + log "warning: error(s) occurred while switching to the new configuration" exit 1 fi fi -if [ "$action" = build-vm -o "$action" = build-vm-with-bootloader ]; then +if [[ "$action" = build-vm || "$action" = build-vm-with-bootloader ]]; then cat >&2 <<EOF -Done. The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm) +Done. The virtual machine can be started by running $(echo "${pathToConfig}/bin/"run-*-vm) EOF fi |