mirror of
https://github.com/nix-community/disko.git
synced 2024-08-15 01:10:28 +03:00
add disko-install command
disko-install combines disko and nixos-install into a single command. Example usage: sudo ./disko-install --flake /someflake#eva --disk sda /dev/zvol/zroot/test
This commit is contained in:
parent
a13f36255c
commit
bde7dd352c
169
disko-install
Executable file
169
disko-install
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
showUsage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
-f, --flake FLAKE_URI#ATTR Use the specified flake to install the NixOS configuration.
|
||||
--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.
|
||||
--reference-lock-file FILE Use the specified lock file as a reference for the Nix store.
|
||||
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=
|
||||
diskoAttr=diskoScript
|
||||
declare -A diskMappings
|
||||
|
||||
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")
|
||||
;;
|
||||
--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
|
||||
mode=$2
|
||||
shift
|
||||
;;
|
||||
--option)
|
||||
if [[ $# -lt 3 ]]; then
|
||||
echo "Option $1 requires an argument" >&2
|
||||
exit 1
|
||||
fi
|
||||
nix_args+=(--option "$2" "$3")
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--disk)
|
||||
if [[ $# -lt 3 ]]; then
|
||||
echo "Option $1 requires an argument" >&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}"
|
||||
}
|
||||
|
||||
main() {
|
||||
parseArgs "$@"
|
||||
|
||||
if [[ -z ${flake-} ]]; then
|
||||
echo "Please specify the flake-uri with --flake to use for installation." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
|
||||
flake="${BASH_REMATCH[1]}"
|
||||
flakeAttr="${BASH_REMATCH[2]}"
|
||||
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
|
||||
|
||||
IFS=$'\n' mapfile -t artifacts < <(nix-build "${libexec_dir}"/install-cli.nix \
|
||||
"${nix_args[@]}" \
|
||||
--no-out-link \
|
||||
--impure \
|
||||
--argstr flake "$flake" \
|
||||
--argstr flakeAttr "$flakeAttr" \
|
||||
--argstr rootMountPoint "$mountPoint" \
|
||||
--arg diskMappings "$(serialiaseArrayToNix diskMappings)" \
|
||||
-A installToplevel \
|
||||
-A "$diskoAttr")
|
||||
nixos_system=${artifacts[0]}
|
||||
disko_script=${artifacts[1]}
|
||||
|
||||
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"
|
||||
nixos-install --no-root-password --system "$nixos_system" --root "$mountPoint"
|
||||
}
|
||||
|
||||
main "$@"
|
26
disko-install.nix
Normal file
26
disko-install.nix
Normal file
@ -0,0 +1,26 @@
|
||||
{ stdenvNoCC, makeWrapper, lib }:
|
||||
|
||||
stdenvNoCC.mkDerivation {
|
||||
name = "disko-install";
|
||||
src = ./.;
|
||||
nativeBuildInputs = [
|
||||
makeWrapper
|
||||
];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin $out/share/disko
|
||||
cp -r install-cli.nix $out/share/disko
|
||||
sed \
|
||||
-e "s|libexec_dir=\".*\"|libexec_dir=\"$out/share/disko\"|" \
|
||||
-e "s|#!/usr/bin/env.*|#!/usr/bin/env bash|" \
|
||||
disko-install > $out/bin/disko-install
|
||||
chmod 755 $out/bin/disko-install
|
||||
wrapProgram $out/bin/disko-install
|
||||
'';
|
||||
meta = with lib; {
|
||||
description = "Disko and nixos-install in one command";
|
||||
homepage = "https://github.com/nix-community/disko";
|
||||
license = licenses.mit;
|
||||
maintainers = with maintainers; [ lassulus ];
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
17
flake.nix
17
flake.nix
@ -28,6 +28,7 @@
|
||||
in
|
||||
{
|
||||
disko = pkgs.callPackage ./package.nix { };
|
||||
disko-install = pkgs.callPackage ./disko-install.nix { };
|
||||
default = self.packages.${system}.disko;
|
||||
} // pkgs.lib.optionalAttrs (!pkgs.buildPlatform.isRiscV64) {
|
||||
disko-doc = pkgs.callPackage ./doc.nix { };
|
||||
@ -43,6 +44,11 @@
|
||||
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
|
||||
eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix");
|
||||
});
|
||||
|
||||
disko-install = pkgs.callPackage ./tests/disko-install {
|
||||
inherit self;
|
||||
};
|
||||
|
||||
shellcheck = pkgs.runCommand "shellcheck" { nativeBuildInputs = [ pkgs.shellcheck ]; } ''
|
||||
cd ${./.}
|
||||
shellcheck disk-deactivate/disk-deactivate disko
|
||||
@ -50,8 +56,17 @@
|
||||
'';
|
||||
in
|
||||
# FIXME: aarch64-linux seems to hang on boot
|
||||
nixpkgs.lib.optionalAttrs pkgs.hostPlatform.isx86_64 nixosTests //
|
||||
nixpkgs.lib.optionalAttrs pkgs.hostPlatform.isx86_64 (nixosTests // { inherit disko-install; }) //
|
||||
pkgs.lib.optionalAttrs (!pkgs.buildPlatform.isRiscV64 && !pkgs.hostPlatform.isx86_32) { inherit shellcheck; });
|
||||
|
||||
nixosConfigurations.testmachine = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
./tests/disko-install/configuration.nix
|
||||
./example/hybrid.nix
|
||||
./module.nix
|
||||
];
|
||||
};
|
||||
formatter = forAllSystems (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
43
install-cli.nix
Normal file
43
install-cli.nix
Normal file
@ -0,0 +1,43 @@
|
||||
{ flake
|
||||
, flakeAttr
|
||||
, diskMappings
|
||||
, canTouchEfiVariables ? false
|
||||
, rootMountPoint ? "/mnt"
|
||||
}:
|
||||
let
|
||||
originalSystem = (builtins.getFlake "${flake}").nixosConfigurations."${flakeAttr}";
|
||||
diskoSystem =
|
||||
let
|
||||
lib = originalSystem.lib;
|
||||
|
||||
modifiedDisks = builtins.mapAttrs
|
||||
(name: value: let
|
||||
dev = if diskMappings ? ${name} then
|
||||
diskMappings.${name}
|
||||
else
|
||||
throw "No device passed for disk '${name}'";
|
||||
in value // {
|
||||
device = dev;
|
||||
content = value.content // { device = dev; };
|
||||
})
|
||||
originalSystem.config.disko.devices.disk;
|
||||
|
||||
cleanedDisks = lib.filterAttrsRecursive (n: _: !lib.hasPrefix "_" n) modifiedDisks;
|
||||
in
|
||||
originalSystem.extendModules {
|
||||
modules = [{
|
||||
disko.rootMountPoint = rootMountPoint;
|
||||
disko.devices.disk = lib.mkVMOverride cleanedDisks;
|
||||
}];
|
||||
};
|
||||
installSystem = originalSystem.extendModules {
|
||||
modules = [({ lib, ... }: {
|
||||
boot.loader.efi.canTouchEfiVariables = lib.mkVMOverride canTouchEfiVariables;
|
||||
boot.loader.grub.devices = lib.mkVMOverride diskoSystem.config.boot.loader.grub.devices;
|
||||
})];
|
||||
};
|
||||
in
|
||||
{
|
||||
installToplevel = installSystem.config.system.build.toplevel;
|
||||
inherit (diskoSystem.config.system.build) formatScript mountScript diskoScript;
|
||||
}
|
23
tests/disko-install/configuration.nix
Normal file
23
tests/disko-install/configuration.nix
Normal file
@ -0,0 +1,23 @@
|
||||
{ lib, pkgs, modulesPath, ... }: {
|
||||
imports = [
|
||||
(modulesPath + "/testing/test-instrumentation.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
(modulesPath + "/profiles/minimal.nix")
|
||||
];
|
||||
|
||||
networking.hostName = "disko-machine";
|
||||
|
||||
# do not try to fetch stuff from the internet
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = 3;
|
||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
};
|
||||
services.openssh.enable = true;
|
||||
boot.kernelParams = [ "console=tty0" ] ++
|
||||
(lib.optional (pkgs.stdenv.hostPlatform.isAarch) "ttyAMA0,115200") ++
|
||||
(lib.optional (pkgs.stdenv.hostPlatform.isRiscV64) "ttySIF0,115200") ++
|
||||
[ "console=ttyS0,115200" ];
|
||||
}
|
45
tests/disko-install/default.nix
Normal file
45
tests/disko-install/default.nix
Normal file
@ -0,0 +1,45 @@
|
||||
{ pkgs ? import <nixpkgs> { }, self }:
|
||||
let
|
||||
disko-install = pkgs.callPackage ../../disko-install.nix { };
|
||||
toplevel = self.nixosConfigurations.testmachine.config.system.build.toplevel;
|
||||
|
||||
dependencies = [
|
||||
pkgs.stdenv.drvPath
|
||||
toplevel
|
||||
self.nixosConfigurations.testmachine.config.system.build.diskoScript
|
||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
in
|
||||
pkgs.nixosTest {
|
||||
name = "disko-test";
|
||||
nodes.machine = {
|
||||
virtualisation.emptyDiskImages = [ 4096 ];
|
||||
virtualisation.memorySize = 3000;
|
||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
def create_test_machine(oldmachine=None, args={}): # taken from <nixpkgs/nixos/tests/installer.nix>
|
||||
machine = create_machine({
|
||||
"qemuFlags":
|
||||
'-cpu max -m 1024 -virtfs local,path=/nix/store,security_model=none,mount_tag=nix-store,'
|
||||
f' -drive file={oldmachine.state_dir}/empty0.qcow2,id=drive1,if=none,index=1,werror=report'
|
||||
f' -device virtio-blk-pci,drive=drive1',
|
||||
} | args)
|
||||
driver.machines.append(machine)
|
||||
return machine
|
||||
machine.succeed("lsblk >&2")
|
||||
|
||||
print(machine.succeed("tty"))
|
||||
machine.succeed("${disko-install}/bin/disko-install --disk main /dev/vdb --flake ${../..}#testmachine")
|
||||
# test idempotency
|
||||
machine.succeed("${disko-install}/bin/disko-install --mode mount --disk main /dev/vdb --flake ${../..}#testmachine")
|
||||
machine.shutdown()
|
||||
|
||||
new_machine = create_test_machine(oldmachine=machine, args={ "name": "after_install" })
|
||||
new_machine.start()
|
||||
name = new_machine.succeed("hostname").strip()
|
||||
assert name == "disko-machine", f"expected hostname 'disko-machine', got {name}"
|
||||
'';
|
||||
}
|
Loading…
Reference in New Issue
Block a user