#!/usr/bin/env bash set -euo pipefail showUsage() { cat <&2 exit 1 fi case $2 in format) diskoAttr=diskoScript ;; mount) diskoAttr=mountScript ;; *) echo "Invalid mode: $2" >&2 echo "Valid modes are: format, mount" >&2 exit 1 ;; esac shift ;; --system-config) if [[ $# -lt 2 ]]; then echo "Option $1 requires one JSON argument." >&2 exit 1 fi # shellcheck disable=SC2034 extraSystemConfig="$2" shift ;; --extra-files) if [[ $# -lt 3 ]]; then echo "Option $1 requires two arguments: source, destination" >&2 exit 1 fi extraFiles[$2]=$3 shift shift ;; --option) if [[ $# -lt 3 ]]; then echo "Option $1 requires two arguments: key, value" >&2 exit 1 fi nix_args+=(--option "$2" "$3") shift shift ;; --disk) if [[ $# -lt 3 ]]; then echo "Option $1 requires two arguments: disk_name, device_path" >&2 exit 1 fi # shellcheck disable=SC2034 diskMappings[$2]=$3 shift shift ;; --mount-point) if [[ $# -lt 2 ]]; then echo "Option $1 requires an argument" >&2 exit 1 fi mountPoint=$2 shift ;; *) echo "Unknown option: $1" >&2 showUsage exit 1 ;; esac shift done } cleanupMountPoint() { if mountpoint -q "${mountPoint}"; then umount -R "${mountPoint}" fi rmdir "${mountPoint}" } nixBuild() { if command -v nom-build > /dev/null; then nom-build "$@" else nix-build "$@" fi } maybeRun () { if [[ -z ${dry_run-} ]]; then "$@" else echo "Would run: $*" fi } main() { parseArgs "$@" if [[ -z ${flake-} ]]; then echo "Please specify the flake-uri with --flake to use for installation." >&2 exit 1 fi # check if we are root if [[ "$EUID" -ne 0 ]] && [[ -z ${dry_run-} ]]; then echo "This script must be run as root" >&2 exit 1 fi if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then flake="${BASH_REMATCH[1]}" flakeAttr="${BASH_REMATCH[2]}" fi if [[ -e "$flake" ]]; then flake=$(realpath "$flake") fi if [[ -z ${flakeAttr-} ]]; then echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri." >&2 echo 'For example, to use the output nixosConfigurations.foo from the flake.nix, append "#foo" to the flake-uri.' >&2 exit 1 fi maybeRun mkdir -p "${mountPoint}" maybeRun chmod 755 "${mountPoint}" # bcachefs wants 755 # shellcheck disable=SC2064 if [[ -z ${dry_run-} ]]; then trap cleanupMountPoint EXIT fi outputs=$(nixBuild "${libexec_dir}"/install-cli.nix \ "${nix_args[@]}" \ --no-out-link \ --impure \ --argstr flake "$flake" \ --argstr flakeAttr "$flakeAttr" \ --argstr rootMountPoint "$mountPoint" \ --arg writeEfiBootEntries "$writeEfiBootEntries" \ --arg diskMappings "$(serialiaseArrayToNix diskMappings)" \ --argstr extraSystemConfig "$extraSystemConfig" \ -A installToplevel \ -A closureInfo \ -A "$diskoAttr") IFS=$'\n' mapfile -t artifacts <<<"$outputs" nixos_system=${artifacts[0]} closure_info=${artifacts[1]} disko_script=${artifacts[2]} if [[ -n ${dry_run-} ]]; then echo "Would run: $disko_script" echo "Would run: nixos-install --system '$nixos_system' --root '$mountPoint'" exit 0 fi # We don't want swap as can break your running system in weird ways if you eject the disk # Hopefully disko-install has enough RAM to run without swap, otherwise we can make this configurable in future. DISKO_SKIP_SWAP=1 "$disko_script" for source in "${!extraFiles[@]}"; do destination=${extraFiles[$source]} mkdir -p "$mountPoint/$(dirname "$destination")" cp -ar "$source" "$mountPoint/$destination" done # nix copy uses up a lot of memory and we work around issues with incorrect checksums in our store # that can be caused by using closureInfo in combination with multiple builders and non-deterministic builds. # Therefore if we have a blank store, we copy the store paths and registration from the closureInfo. if [[ ! -f "${mountPoint}/nix/var/nix/db/db.sqlite" ]]; then echo "Copying store paths" >&2 mkdir -p "${mountPoint}/nix/store" xargs cp --recursive --target "${mountPoint}/nix/store" < "${closure_info}/store-paths" echo "Loading nix database" >&2 NIX_STATE_DIR=${mountPoint}/nix/var/nix nix-store --load-db < "${closure_info}/registration" fi nixos-install --no-channel-copy --no-root-password --system "$nixos_system" --root "$mountPoint" } if main "$@"; then echo "disko-install succeeded" exit 0 else echo "disko-install failed" >&2 exit 1 fi