mirror of
https://github.com/nix-community/disko
synced 2024-11-13 23:57:12 +00:00
9ab6ae4e63
Fixes #827
247 lines
6.6 KiB
Bash
Executable file
247 lines
6.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
showUsage() {
|
|
cat <<EOF
|
|
Usage: $0 [OPTIONS]
|
|
--mode MODE Specify the mode of operation. Valid modes are: format, mount.
|
|
Format will format the disk before installing.
|
|
Mount will mount the disk before installing.
|
|
Mount is useful for updating an existing system without losing data.
|
|
-f, --flake FLAKE_URI#ATTR Use the specified flake to install the NixOS configuration.
|
|
--disk NAME DEVICE Map the specified disk name to the specified device path.
|
|
--dry-run Print the commands that would be run, but do not run them.
|
|
--show-trace Show the stack trace on error.
|
|
-h, --help Show this help message.
|
|
--extra-files SOURCE DEST Copy the specified file or directory from the host into the NixOS configuration.
|
|
--option NAME VALUE Pass the specified option to Nix.
|
|
--write-efi-boot-entries Write EFI boot entries to the NVRAM of the system for the installed system.
|
|
Specify this option if you plan to boot from this disk on the current machine,
|
|
but not if you plan to move the disk to another machine.
|
|
--system-config JSON Merges the specified JSON object into the NixOS configuration.
|
|
EOF
|
|
}
|
|
|
|
serialiaseArrayToNix() {
|
|
local -n array=$1
|
|
nixExpr="{ "
|
|
# Iterate over the associative array to populate the Nix attrset string
|
|
for key in "${!array[@]}"; do
|
|
value=${array[$key]}
|
|
nixExpr+="\"${key//\"/\\\"}\" = \"${value//\"/\\\"}\"; "
|
|
done
|
|
nixExpr+="}"
|
|
|
|
echo "$nixExpr"
|
|
}
|
|
|
|
readonly libexec_dir="${0%/*}"
|
|
|
|
nix_args=(
|
|
--extra-experimental-features 'nix-command flakes'
|
|
"--option" "no-write-lock-file" "true"
|
|
)
|
|
dry_run=
|
|
extraSystemConfig="{}"
|
|
diskoAttr=diskoScript
|
|
writeEfiBootEntries=false
|
|
declare -A diskMappings
|
|
declare -A extraFiles
|
|
|
|
|
|
parseArgs() {
|
|
[[ $# -eq 0 ]] && {
|
|
showUsage
|
|
exit 1
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-d | --debug)
|
|
set -x
|
|
;;
|
|
-f | --flake)
|
|
flake=$2
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
showUsage
|
|
exit 0
|
|
;;
|
|
--dry-run)
|
|
dry_run=y
|
|
;;
|
|
--show-trace)
|
|
nix_args+=("$1")
|
|
;;
|
|
--write-efi-boot-entries)
|
|
writeEfiBootEntries=true
|
|
;;
|
|
--mode)
|
|
if [[ $# -lt 2 ]]; then
|
|
echo "Option $1 requires an argument" >&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="/mnt/disko-install-root"
|
|
mkdir -p "${mountPoint}"
|
|
chmod 755 "${mountPoint}" # bcachefs 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 "$@"
|