mirror of
https://github.com/nix-community/disko.git
synced 2024-11-04 05:44:29 +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
|
in
|
||||||
{
|
{
|
||||||
disko = pkgs.callPackage ./package.nix { };
|
disko = pkgs.callPackage ./package.nix { };
|
||||||
|
disko-install = pkgs.callPackage ./disko-install.nix { };
|
||||||
default = self.packages.${system}.disko;
|
default = self.packages.${system}.disko;
|
||||||
} // pkgs.lib.optionalAttrs (!pkgs.buildPlatform.isRiscV64) {
|
} // pkgs.lib.optionalAttrs (!pkgs.buildPlatform.isRiscV64) {
|
||||||
disko-doc = pkgs.callPackage ./doc.nix { };
|
disko-doc = pkgs.callPackage ./doc.nix { };
|
||||||
@ -43,6 +44,11 @@
|
|||||||
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
|
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
|
||||||
eval-config = import (pkgs.path + "/nixos/lib/eval-config.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 ]; } ''
|
shellcheck = pkgs.runCommand "shellcheck" { nativeBuildInputs = [ pkgs.shellcheck ]; } ''
|
||||||
cd ${./.}
|
cd ${./.}
|
||||||
shellcheck disk-deactivate/disk-deactivate disko
|
shellcheck disk-deactivate/disk-deactivate disko
|
||||||
@ -50,8 +56,17 @@
|
|||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
# FIXME: aarch64-linux seems to hang on boot
|
# 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; });
|
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:
|
formatter = forAllSystems (system:
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
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