1#! @shell@ 2 3if [ -x "@shell@" ]; then export SHELL="@shell@"; fi; 4 5set -e 6 7showSyntax() { 8 exec man nixos-rebuild 9 exit 1 10} 11 12 13# Parse the command line. 14origArgs=("$@") 15extraBuildFlags=() 16action= 17buildNix=1 18rollback= 19upgrade= 20repair= 21profile=/nix/var/nix/profiles/system 22buildHost= 23targetHost= 24 25while [ "$#" -gt 0 ]; do 26 i="$1"; shift 1 27 case "$i" in 28 --help) 29 showSyntax 30 ;; 31 switch|boot|test|build|dry-build|dry-run|dry-activate|build-vm|build-vm-with-bootloader) 32 if [ "$i" = dry-run ]; then i=dry-build; fi 33 action="$i" 34 ;; 35 --install-grub) 36 export NIXOS_INSTALL_GRUB=1 37 ;; 38 --no-build-nix) 39 buildNix= 40 ;; 41 --rollback) 42 rollback=1 43 ;; 44 --upgrade) 45 upgrade=1 46 ;; 47 --repair) 48 repair=1 49 extraBuildFlags+=("$i") 50 ;; 51 --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q) 52 extraBuildFlags+=("$i") 53 ;; 54 --max-jobs|-j|--cores|-I) 55 j="$1"; shift 1 56 extraBuildFlags+=("$i" "$j") 57 ;; 58 --option) 59 j="$1"; shift 1 60 k="$1"; shift 1 61 extraBuildFlags+=("$i" "$j" "$k") 62 ;; 63 --fast) 64 buildNix= 65 extraBuildFlags+=(--show-trace) 66 ;; 67 --profile-name|-p) 68 if [ -z "$1" ]; then 69 echo "$0: ‘--profile-name’ requires an argument" 70 exit 1 71 fi 72 if [ "$1" != system ]; then 73 profile="/nix/var/nix/profiles/system-profiles/$1" 74 mkdir -p -m 0755 "$(dirname "$profile")" 75 fi 76 shift 1 77 ;; 78 --build-host|h) 79 buildHost="$1" 80 shift 1 81 ;; 82 --target-host|t) 83 targetHost="$1" 84 shift 1 85 ;; 86 *) 87 echo "$0: unknown option \`$i'" 88 exit 1 89 ;; 90 esac 91done 92 93 94if [ -z "$buildHost" -a -n "$targetHost" ]; then 95 buildHost="$targetHost" 96fi 97if [ "$targetHost" = localhost ]; then 98 targetHost= 99fi 100if [ "$buildHost" = localhost ]; then 101 buildHost= 102fi 103 104buildHostCmd() { 105 if [ -z "$buildHost" ]; then 106 "$@" 107 elif [ -n "$remoteNix" ]; then 108 ssh $SSHOPTS "$buildHost" PATH="$remoteNix:$PATH" "$@" 109 else 110 ssh $SSHOPTS "$buildHost" "$@" 111 fi 112} 113 114targetHostCmd() { 115 if [ -z "$targetHost" ]; then 116 "$@" 117 else 118 ssh $SSHOPTS "$targetHost" "$@" 119 fi 120} 121 122copyToTarget() { 123 if ! [ "$targetHost" = "$buildHost" ]; then 124 if [ -z "$targetHost" ]; then 125 NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --from "$buildHost" "$1" 126 elif [ -z "$buildHost" ]; then 127 NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$targetHost" "$1" 128 else 129 buildHostCmd nix-copy-closure --to "$targetHost" "$1" 130 fi 131 fi 132} 133 134nixBuild() { 135 if [ -z "$buildHost" ]; then 136 nix-build "$@" 137 else 138 local instArgs=() 139 local buildArgs=() 140 141 while [ "$#" -gt 0 ]; do 142 local i="$1"; shift 1 143 case "$i" in 144 -o) 145 local out="$1"; shift 1 146 buildArgs+=("--add-root" "$out" "--indirect") 147 ;; 148 -A) 149 local j="$1"; shift 1 150 instArgs+=("$i" "$j") 151 ;; 152 -I) # We don't want this in buildArgs 153 shift 1 154 ;; 155 --no-out-link) # We don't want this in buildArgs 156 ;; 157 "<"*) # nix paths 158 instArgs+=("$i") 159 ;; 160 *) 161 buildArgs+=("$i") 162 ;; 163 esac 164 done 165 166 local drv="$(nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")" 167 if [ -a "$drv" ]; then 168 NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$buildHost" "$drv" 169 buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" 170 else 171 echo "nix-instantiate failed" 172 exit 1 173 fi 174 fi 175} 176 177 178if [ -z "$action" ]; then showSyntax; fi 179 180# Only run shell scripts from the Nixpkgs tree if the action is 181# "switch", "boot", or "test". With other actions (such as "build"), 182# the user may reasonably expect that no code from the Nixpkgs tree is 183# executed, so it's safe to run nixos-rebuild against a potentially 184# untrusted tree. 185canRun= 186if [ "$action" = switch -o "$action" = boot -o "$action" = test ]; then 187 canRun=1 188fi 189 190 191# If ‘--upgrade’ is given, run ‘nix-channel --update nixos’. 192if [ -n "$upgrade" -a -z "$_NIXOS_REBUILD_REEXEC" ]; then 193 nix-channel --update nixos 194 195 # If there are other channels that contain a file called 196 # ".update-on-nixos-rebuild", update them as well. 197 for channelpath in /nix/var/nix/profiles/per-user/root/channels/*; do 198 if [ -e "$channelpath/.update-on-nixos-rebuild" ]; then 199 nix-channel --update "$(basename "$channelpath")" 200 fi 201 done 202fi 203 204# Make sure that we use the Nix package we depend on, not something 205# else from the PATH for nix-{env,instantiate,build}. This is 206# important, because NixOS defaults the architecture of the rebuilt 207# system to the architecture of the nix-* binaries used. So if on an 208# amd64 system the user has an i686 Nix package in her PATH, then we 209# would silently downgrade the whole system to be i686 NixOS on the 210# next reboot. 211if [ -z "$_NIXOS_REBUILD_REEXEC" ]; then 212 export PATH=@nix@/bin:$PATH 213fi 214 215# Re-execute nixos-rebuild from the Nixpkgs tree. 216if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" ]; then 217 if p=$(nix-instantiate --find-file nixpkgs/nixos/modules/installer/tools/nixos-rebuild.sh "${extraBuildFlags[@]}"); then 218 export _NIXOS_REBUILD_REEXEC=1 219 exec $SHELL -e $p "${origArgs[@]}" 220 exit 1 221 fi 222fi 223 224 225tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX) 226SSHOPTS="$NIX_SSHOPTS -o ControlMaster=auto -o ControlPath=$tmpDir/ssh-%n -o ControlPersist=60" 227 228cleanup() { 229 for ctrl in "$tmpDir"/ssh-*; do 230 ssh -o ControlPath="$ctrl" -O exit dummyhost 2>/dev/null || true 231 done 232 rm -rf "$tmpDir" 233} 234trap cleanup EXIT 235 236 237 238# If the Nix daemon is running, then use it. This allows us to use 239# the latest Nix from Nixpkgs (below) for expression evaluation, while 240# still using the old Nix (via the daemon) for actual store access. 241# This matters if the new Nix in Nixpkgs has a schema change. It 242# would upgrade the schema, which should only happen once we actually 243# switch to the new configuration. 244# If --repair is given, don't try to use the Nix daemon, because the 245# flag can only be used directly. 246if [ -z "$repair" ] && systemctl show nix-daemon.socket nix-daemon.service | grep -q ActiveState=active; then 247 export NIX_REMOTE=${NIX_REMOTE:-daemon} 248fi 249 250 251# First build Nix, since NixOS may require a newer version than the 252# current one. 253if [ -n "$rollback" -o "$action" = dry-build ]; then 254 buildNix= 255fi 256 257prebuiltNix() { 258 machine="$1" 259 if [ "$machine" = x86_64 ]; then 260 return /nix/store/xryr9g56h8yjddp89d6dw12anyb4ch7c-nix-1.10 261 elif [[ "$machine" =~ i.86 ]]; then 262 return /nix/store/2w92k5wlpspf0q2k9mnf2z42prx3bwmv-nix-1.10 263 else 264 echo "$0: unsupported platform" 265 exit 1 266 fi 267} 268 269remotePATH= 270 271if [ -n "$buildNix" ]; then 272 echo "building Nix..." >&2 273 nixDrv= 274 if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A config.nix.package "${extraBuildFlags[@]}")"; then 275 if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A nixFallback "${extraBuildFlags[@]}")"; then 276 if ! nixDrv="$(nix-instantiate '<nixpkgs>' --add-root $tmpDir/nix.drv --indirect -A nix "${extraBuildFlags[@]}")"; then 277 nixStorePath="$(prebuiltNix "$(uname -m)")" 278 if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \ 279 --option extra-binary-caches https://cache.nixos.org/; then 280 echo "warning: don't know how to get latest Nix" >&2 281 fi 282 # Older version of nix-store -r don't support --add-root. 283 [ -e $tmpDir/nix ] || ln -sf $nixStorePath $tmpDir/nix 284 if [ -n "$buildHost" ]; then 285 remoteNixStorePath="$(prebuiltNix "$(buildHostCmd uname -m)")" 286 remoteNix="$remoteNixStorePath/bin" 287 if ! buildHostCmd nix-store -r $remoteNixStorePath \ 288 --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then 289 remoteNix= 290 echo "warning: don't know how to get latest Nix" >&2 291 fi 292 fi 293 fi 294 fi 295 fi 296 if [ -a "$nixDrv" ]; then 297 nix-store -r "$nixDrv"'!'"out" --add-root $tmpDir/nix --indirect >/dev/null 298 if [ -n "$buildHost" ]; then 299 nix-copy-closure --to "$buildHost" "$nixDrv" 300 # The nix build produces multiple outputs, we add them all to the remote path 301 for p in $(buildHostCmd nix-store -r "$(readlink "$nixDrv")" "${buildArgs[@]}"); do 302 remoteNix="$remoteNix${remoteNix:+:}$p/bin" 303 done 304 fi 305 fi 306 PATH="$tmpDir/nix/bin:$PATH" 307fi 308 309 310# Update the version suffix if we're building from Git (so that 311# nixos-version shows something useful). 312if [ -n "$canRun" ]; then 313 if nixpkgs=$(nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then 314 suffix=$($SHELL $nixpkgs/nixos/modules/installer/tools/get-version-suffix "${extraBuildFlags[@]}" || true) 315 if [ -n "$suffix" ]; then 316 echo -n "$suffix" > "$nixpkgs/.version-suffix" || true 317 fi 318 fi 319fi 320 321 322if [ "$action" = dry-build ]; then 323 extraBuildFlags+=(--dry-run) 324fi 325 326 327# Either upgrade the configuration in the system profile (for "switch" 328# or "boot"), or just build it and create a symlink "result" in the 329# current directory (for "build" and "test"). 330if [ -z "$rollback" ]; then 331 echo "building the system configuration..." >&2 332 if [ "$action" = switch -o "$action" = boot ]; then 333 pathToConfig="$(nixBuild '<nixpkgs/nixos>' --no-out-link -A system "${extraBuildFlags[@]}")" 334 copyToTarget "$pathToConfig" 335 targetHostCmd nix-env -p "$profile" --set "$pathToConfig" 336 elif [ "$action" = test -o "$action" = build -o "$action" = dry-build -o "$action" = dry-activate ]; then 337 pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A system -k "${extraBuildFlags[@]}")" 338 elif [ "$action" = build-vm ]; then 339 pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A vm -k "${extraBuildFlags[@]}")" 340 elif [ "$action" = build-vm-with-bootloader ]; then 341 pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A vmWithBootLoader -k "${extraBuildFlags[@]}")" 342 else 343 showSyntax 344 fi 345 # Copy build to target host if we haven't already done it 346 if ! [ "$action" = switch -o "$action" = boot ]; then 347 copyToTarget "$pathToConfig" 348 fi 349else # [ -n "$rollback" ] 350 if [ "$action" = switch -o "$action" = boot ]; then 351 targetHostCmd nix-env --rollback -p "$profile" 352 pathToConfig="$profile" 353 elif [ "$action" = test -o "$action" = build ]; then 354 systemNumber=$( 355 targetHostCmd nix-env -p "$profile" --list-generations | 356 sed -n '/current/ {g; p;}; s/ *\([0-9]*\).*/\1/; h' 357 ) 358 pathToConfig="$profile"-${systemNumber}-link 359 if [ -z "$targetHost" ]; then 360 ln -sT "$pathToConfig" ./result 361 fi 362 else 363 showSyntax 364 fi 365fi 366 367 368# If we're not just building, then make the new configuration the boot 369# default and/or activate it now. 370if [ "$action" = switch -o "$action" = boot -o "$action" = test -o "$action" = dry-activate ]; then 371 if ! targetHostCmd $pathToConfig/bin/switch-to-configuration "$action"; then 372 echo "warning: error(s) occurred while switching to the new configuration" >&2 373 exit 1 374 fi 375fi 376 377 378if [ "$action" = build-vm ]; then 379 cat >&2 <<EOF 380 381Done. The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm). 382EOF 383fi