#!/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 ;; *) echo "Unknown option: $1" >&2 showUsage exit 1 ;; esac shift done } cleanupMountPoint() { mountPoint=$1 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 } 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 ]]; 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 mountPoint=$(mktemp -d) chmod 755 "${mountPoint}" # bchachefs wants 755 escapeMountPoint=$(printf '%q' "$mountPoint") # shellcheck disable=SC2064 trap "cleanupMountPoint ${escapeMountPoint}" EXIT 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 "$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 [[ ! -d "${mountPoint}/nix/store" ]]; 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" } main "$@"