move example hosts and overwrite to /example/, misc

This commit is contained in:
Niklas Gollenstede 2024-08-28 13:12:07 +02:00
parent 95aa261987
commit c8addc3f98
15 changed files with 60 additions and 44 deletions

View File

@ -147,6 +147,7 @@
"partlabel", // linux "partlabel", // linux
"partprobe", // program / function "partprobe", // program / function
"partuuid", // linux "partuuid", // linux
"pbkdf", // option
"pflash", // cli arg "pflash", // cli arg
"pipefail", // bash "pipefail", // bash
"pkgs", // nix "pkgs", // nix

View File

@ -36,7 +36,7 @@ in { preface = { # (any »preface« options have to be defined here)
boot.loader.grub.enable = false; boot.loader.grub.enable = false;
# Example of adding and/or overwriting setup/maintenance functions: # Example of adding and/or overwriting setup/maintenance functions:
#installer.scripts.install-overwrite = { path = ../example/install.sh.md; order = 1500; }; #installer.scripts.install-overwrite.path = ../lib/install.sh.md;
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;

View File

@ -4,7 +4,7 @@
# Installer Script Overrides (Example) # Installer Script Overrides (Example)
This is an example on how to customize the default installation process. This is an example on how to customize the default installation process.
The [`config.installer.commands.*`](../modules/installer.nix.md) can be used for some per-host customization, but for further reaching changes that are supposed to affect all hosts in a configuration, it may be necessary or more appropriate to extend/override the [default installer functions](../lib/setup-scripts/). The [`config.installer.commands.*`](../../modules/installer.nix.md) can be used for some per-host customization, but for further reaching changes that are supposed to affect all hosts in a configuration, it may be necessary or more appropriate to extend/override the [default installer functions](../../lib/setup-scripts/).
This file would need to be included in the configuration like this: This file would need to be included in the configuration like this:
```nix ```nix
@ -32,7 +32,7 @@ declare-command my-thing withThese coolArguments << 'EOD'
This does my thing with these cool arguments -- duh! This does my thing with these cool arguments -- duh!
EOD EOD
declare-flag my-thing laser-color COLOR "Color of the laser used for my thing." declare-flag my-thing laser-color COLOR "Color of the laser used for my thing."
function run-qemu { # 1: withThese, 2: coolArguments function my-thing { # 1: withThese, 2: coolArguments
local withThese=$1 local withThese=$1
local coolThings=$2 local coolThings=$2
echo "I am playing $withThese to shoot $coolArguments with ${args[laser-color]:-red} lasers!" echo "I am playing $withThese to shoot $coolArguments with ${args[laser-color]:-red} lasers!"

Binary file not shown.

View File

@ -6,17 +6,18 @@
functions = { url = "github:NiklasGollenstede/nix-functions"; inputs.nixpkgs.follows = "nixpkgs"; }; functions = { url = "github:NiklasGollenstede/nix-functions"; inputs.nixpkgs.follows = "nixpkgs"; };
config.url = "github:NiklasGollenstede/nixos-installer?dir=example/defaultConfig"; # "path:./example/defaultConfig"; # (The latter only works on each host after using this flake directly (not as dependency or another flake). The former effectively points to the last commit, i.e. it takes two commits to apply changes to the default config.) config.url = "github:NiklasGollenstede/nixos-installer?dir=example/defaultConfig"; # "path:./example/defaultConfig"; # (The latter only works on each host after using this flake directly (not as dependency or another flake). The former effectively points to the last commit, i.e. it takes two commits to apply changes to the default config.)
}; outputs = inputs@{ self, ... }: inputs.functions.lib.importRepo inputs ./. (repo@{ overlays, ... }: let }; outputs = inputs@{ self, ... }: inputs.functions.lib.importRepo inputs ./. (repo': let
repo = repo'.override { applyToPackages = pkgs: packages: builtins.removeAttrs packages [ "libblockdev" ]; };
lib = repo.lib.__internal__; lib = repo.lib.__internal__;
in [ # Run »nix flake show --allow-import-from-derivation« to see what this merges to: in [ # Run »nix flake show --allow-import-from-derivation« to see what this merges to:
## Exports (things to reuse in other flakes): ## Exports (things to reuse in other flakes):
(repo // { packages = lib.fun.packagesFromOverlay { inherit inputs; exclude = [ "libblockdev" ]; }; }) # lib.* nixosModules.* overlays.* packages.* repo # lib.* nixosModules.* overlays.* packages.*
## Examples: ## Examples:
# The example host definitions from ./hosts/, plus their installers (apps): # The example host definitions from ./hosts/, plus their installers (apps):
(lib.self.mkSystemsFlake { inherit inputs; asDefaultPackage = true; }) # nixosConfigurations.* apps.*-linux.* devShells.*-linux.* packages.*-linux.all-systems/default (lib.self.mkSystemsFlake { inherit inputs; hosts.dir = "${self}/example/hosts"; asDefaultPackage = true; }) # nixosConfigurations.* apps.*-linux.* devShells.*-linux.* packages.*-linux.all-systems/default
# The same cross-compiled from aarch64 (just to show how that works): # The same cross-compiled from aarch64 (just to show how that works):
(lib.self.mkSystemsFlake { inherit inputs; buildPlatform = "aarch64-linux"; renameOutputs = name: "arm:${name}"; }) # nixosConfigurations.arm:* apps.*-linux.arm:* devShells.*-linux.arm:* packages.*-linux.arm:all-systems (lib.self.mkSystemsFlake { inherit inputs; hosts.dir = "${self}/example/hosts"; buildPlatform = "aarch64-linux"; renameOutputs = name: "arm:${name}"; }) # nixosConfigurations.arm:* apps.*-linux.arm:* devShells.*-linux.arm:* packages.*-linux.arm:all-systems
]); } ]); }

View File

@ -28,20 +28,26 @@ in rec {
extraModules ? [ ], moduleArgs ? { }, nixosArgs ? { }, extraModules ? [ ], moduleArgs ? { }, nixosArgs ? { },
nixosSystem ? inputs.nixpkgs.lib.nixosSystem, nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
buildPlatform ? null, buildPlatform ? null,
}: nixosSystem (nixosArgs // { }: nixosSystem (nixosArgs // (let args = {
#system = null; # (This actually does nothing more than setting »config.nixpkgs.system« (which is the same as »config.nixpkgs.buildPlatform.system«) and can be null/unset here.) #system = null; # (This actually does nothing more than setting »config.nixpkgs.system« (which is the same as »config.nixpkgs.buildPlatform.system«) and can be null/unset here.)
modules = (nixosArgs.modules or [ ]) ++ [ { imports = [ # Anything specific to only this evaluation of the module tree should go here. modules = (nixosArgs.modules or [ ]) ++ [ { imports = [ # Anything specific to only this evaluation of the module tree should go here.
(let (let # mainModule
module = if (builtins.isPath mainModule) || (builtins.isString mainModule) then (importWrapped inputs mainModule).module else mainModule; module = if (builtins.isPath mainModule) || (builtins.isString mainModule) then (importWrapped inputs mainModule).module else mainModule;
bindName = func: lib.setFunctionArgs (args: func (args // { inherit name; })) (builtins.removeAttrs (lib.functionArgs func) [ "name" ]); # ensure that in the main module, the "name" parameter is available during the import stage already:
bindToModule = module: if lib.isFunction module then bindName module else if module?imports then module // { imports = map bindToModule module.imports; } else module; bindName2function = func: lib.setFunctionArgs (args: func (args // { inherit name; })) (builtins.removeAttrs (lib.functionArgs func) [ "name" ]);
in bindToModule module) # ensure that in the main module, the "name" parameter is available during the import stage already bindName2module = module: if lib.isFunction module then bindName2function module else if module?imports then module // { imports = map bindName2module module.imports; } else module;
in bindName2module module)
{ _module.args.name = lib.mkOverride 0 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect) { _module.args.name = lib.mkOverride 0 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect)
{ networking.hostName = name; } { networking.hostName = name; }
# containers may or may not want to inherit extraModules (but it is easy to add), but there is no reason not to inherit specialArgs:
{ options.containers = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule (_: { config = {
specialArgs = builtins.mapAttrs (_: lib.mkDefault) args.specialArgs; # imports = args.extraModules;
}; })); }; }
# `specializations` use `noUserModules`, which inherit specialArgs and extraModules, or `extendModules` inherits all modules and arguments. VM-variants also use `extendModules`.
]; _file = "${dirname}/nixos.nix#modules"; } ]; ]; _file = "${dirname}/nixos.nix#modules"; } ];
extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ { imports = [ (args: { extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ { imports = [ (_: {
# These are passed as »extraModules« module argument and can thus be reused when defining containers and such (so define as much stuff as possible here). # These are passed as »extraModules« module argument and can thus be reused when defining containers and such (so define as much stuff as possible here).
# There is, unfortunately, no way to directly pass modules into all containers. Each container will need to be defined with »config.containers."${name}".config.imports = extraModules«. # There is, unfortunately, no way to directly pass modules into all containers. Each container will need to be defined with »config.containers."${name}".config.imports = extraModules«.
# (One could do that automatically by defining »options.security.containers = lib.mkOption { type = lib.types.submodule (cfg: { options.config = lib.mkOption { apply = _:_.extendModules { modules = extraModules; }; }); }«.) # (One could do that automatically by defining »options.security.containers = lib.mkOption { type = lib.types.submodule (cfg: { options.config = lib.mkOption { apply = _:_.extendModules { modules = extraModules; }; }); }«.)
@ -57,7 +63,7 @@ in rec {
specialArgs = (nixosArgs.specialArgs or { }) // { inherit inputs; }; specialArgs = (nixosArgs.specialArgs or { }) // { inherit inputs; };
# (This is already set during module import, while »_module.args« only becomes available during module evaluation (before that, using it causes infinite recursion). Since it can't be ensured that this is set in every circumstance where »extraModules« are being used, it should generally not be used to set custom arguments.) # (This is already set during module import, while »_module.args« only becomes available during module evaluation (before that, using it causes infinite recursion). Since it can't be ensured that this is set in every circumstance where »extraModules« are being used, it should generally not be used to set custom arguments.)
}); }; in args));
# Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of base names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided. # Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of base names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided.
# If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »${preface'}.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »moduleArgs« to the modules and can thus be used to adjust the config per instance). # If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »${preface'}.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »moduleArgs« to the modules and can thus be used to adjust the config per instance).
@ -77,11 +83,14 @@ in rec {
in (mapMergeUnique (name: { "${name}" = let in (mapMergeUnique (name: { "${name}" = let
preface = getPreface inputs (moduleArgs // { inherit preface; }) mainModule name; # (call again, with name) preface = getPreface inputs (moduleArgs // { inherit preface; }) mainModule name; # (call again, with name)
in { inherit preface; } // (mkNixosConfiguration (let systemArgs = ( in { inherit preface; } // (mkNixosConfiguration (let systemArgs = (
builtins.removeAttrs args [ "files" "dir" "exclude" ] builtins.removeAttrs args [ "files" "dir" "exclude" "prefix" ]
) // { ) // {
inherit name mainModule; inherit name mainModule;
moduleArgs = (moduleArgs // { inherit preface; }); moduleArgs = (moduleArgs // { inherit preface; });
nixosArgs = (args.nixosArgs or { }) // { specialArgs = (args.nixosArgs.specialArgs or { }) // { inherit preface; }; }; # make this available early, and only for the main evaluation (+specialisations, -containers) nixosArgs = (args.nixosArgs or { }) // {
specialArgs = (args.nixosArgs.specialArgs or { }) // { inherit preface; }; # make this available early, and only for the main evaluation (+specialisations +containers)
prefix = (args.nixosArgs.prefix or [ ]) ++ ((args.prefix or (_: [ ])) name);
};
extraModules = (args.extraModules or [ ]) ++ [ { imports = [ (args: { extraModules = (args.extraModules or [ ]) ++ [ { imports = [ (args: {
options.${preface'} = { options.${preface'} = {
instances = lib.mkOption { description = "List of host names to instantiate this host config for, instead of just for the file name."; type = lib.types.listOf lib.types.str; readOnly = true; } // (lib.optionalAttrs (!preface?instances) { default = instances; }); instances = lib.mkOption { description = "List of host names to instantiate this host config for, instead of just for the file name."; type = lib.types.listOf lib.types.str; readOnly = true; } // (lib.optionalAttrs (!preface?instances) { default = instances; });
@ -123,6 +132,8 @@ in rec {
moduleArgs ? { }, moduleArgs ? { },
# The »nixosSystem« function defined in »<nixpkgs>/flake.nix«, or equivalent. # The »nixosSystem« function defined in »<nixpkgs>/flake.nix«, or equivalent.
nixosSystem ? inputs.nixpkgs.lib.nixosSystem, nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
# Attribute path labels to prepend to option names/paths. Useful for debugging when building multiple systems at once.
prefix ? (name: [ "[${if renameOutputs == false then name else renameOutputs name}]" ]),
# If provided, this will be set as »config.nixpkgs.buildPlatform« for all hosts, which in turn enables cross-compilation for all hosts whose »config.nixpkgs.hostPlatform« (the architecture they will run on) does not expand to the same value. Without this, building for other platforms may still work (slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s). # If provided, this will be set as »config.nixpkgs.buildPlatform« for all hosts, which in turn enables cross-compilation for all hosts whose »config.nixpkgs.hostPlatform« (the architecture they will run on) does not expand to the same value. Without this, building for other platforms may still work (slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s).
buildPlatform ? null, buildPlatform ? null,
## The platforms for which the setup scripts (installation & maintenance/debugging) will be defined. Should include the ».buildPlatform« and/or the target system's »config.nixpkgs.hostPlatform«. ## The platforms for which the setup scripts (installation & maintenance/debugging) will be defined. Should include the ».buildPlatform« and/or the target system's »config.nixpkgs.hostPlatform«.
@ -134,7 +145,7 @@ in rec {
... }: let ... }: let
getName = if renameOutputs == false then (name: name) else renameOutputs; getName = if renameOutputs == false then (name: name) else renameOutputs;
otherArgs = (builtins.removeAttrs args [ "hosts" "moduleInputs" "overlayInputs" "renameOutputs" "asDefaultPackage" ]) // { otherArgs = (builtins.removeAttrs args [ "hosts" "moduleInputs" "overlayInputs" "renameOutputs" "asDefaultPackage" ]) // {
inherit inputs modules overlays moduleArgs nixosSystem buildPlatform extraModules; inherit inputs modules overlays moduleArgs nixosSystem buildPlatform extraModules prefix;
nixosArgs = (args.nixosArgs or { }) // { modules = (args.nixosArgs.modules or [ ]) ++ [ { imports = [ (args: { nixosArgs = (args.nixosArgs or { }) // { modules = (args.nixosArgs.modules or [ ]) ++ [ { imports = [ (args: {
${installer}.outputName = getName args.config._module.args.name; ${installer}.outputName = getName args.config._module.args.name;
}) ]; _file = "${dirname}/nixos.nix#mkSystemsFlake-extraModule"; } ]; }; }) ]; _file = "${dirname}/nixos.nix#mkSystemsFlake-extraModule"; } ]; };

View File

@ -53,7 +53,7 @@ function gen-key-home-composite {( set -eu # 1: usage, 2: user
password=${userPasswords[$user]} password=${userPasswords[$user]}
else else
password=$( prompt-new-password "for the user account »$user« (as component for key »(@{config.networking.hostName}:)$usage«)" ) password=$( prompt-new-password "for the user account »$user« (as component for key »(@{config.networking.hostName}:)$usage«)" )
if [[ ! $password ]] ; then \exit 1 ; fi if [[ ! $password ]] ; then \exit 1 ; fi ; userPasswords[$user]=$password # TODO: won't propagate
fi fi
{ cat "$keystore"/home/"$user".key && cat <<<"$password" ; } | sha256sum | head -c 64 { cat "$keystore"/home/"$user".key && cat <<<"$password" ; } | sha256sum | head -c 64
)} )}
@ -67,7 +67,7 @@ function gen-key-home-yubikey {( set -eu # 1: usage, 2: serialAndSlotAndUser(as
password=${userPasswords[$user]} password=${userPasswords[$user]}
else else
password=$( prompt-new-password "for the user account »$user« (as YubiKey challenge for key »:$usage«)" ) password=$( prompt-new-password "for the user account »$user« (as YubiKey challenge for key »:$usage«)" )
if [[ ! $password ]] ; then \exit 1 ; fi if [[ ! $password ]] ; then \exit 1 ; fi ; userPasswords[$user]=$password # TODO: won't propagate
fi fi
gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$user=$password" true "»${user}«'s password (to create key »:${usage}«)" gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$user=$password" true "»${user}«'s password (to create key »:${usage}«)"
)} )}

View File

@ -193,8 +193,8 @@ function format-partitions {
elif [[ ${fs[device]} == /dev/mapper/* ]] ; then elif [[ ${fs[device]} == /dev/mapper/* ]] ; then
if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${fs[device]/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device ${fs[device]} used by mount ${fs[mountPoint]} does not point at one of the device mappings ${!config.boot.initrd.luks.devices!catAttrSets.device[@]}" 1>&2 ; \return 1 ; fi if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${fs[device]/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device ${fs[device]} used by mount ${fs[mountPoint]} does not point at one of the device mappings ${!config.boot.initrd.luks.devices!catAttrSets.device[@]}" 1>&2 ; \return 1 ; fi
else continue ; fi else continue ; fi
eval 'declare -a formatArgs='"${fs[formatArgs]}" eval 'declare -a formatArgs='"${fs[formatArgs]}" ; eval 'declare -a options='"${fs[options]}"
( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs."${fs[fsType]}" "${formatArgs[@]}" "${fs[device]}" >$beLoud 2>$beSilent ) || [[ $options == *,nofail,* ]] || return ( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs."${fs[fsType]}" "${formatArgs[@]}" "${fs[device]}" >$beLoud 2>$beSilent ) || [[ ' '${options[@]}' ' == *' 'nofail' '* ]] || return
@{native.parted}/bin/partprobe "${fs[device]}" || true @{native.parted}/bin/partprobe "${fs[device]}" || true
done done
for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do

View File

@ -42,6 +42,8 @@ declare-flag install-system no-vm "" "Never perform the installation in a VM. Fa
## Does some argument validation, performs some sanity checks, includes a hack to make installation work when nix isn't installed for root, and runs the installation in qemu (if requested). ## Does some argument validation, performs some sanity checks, includes a hack to make installation work when nix isn't installed for root, and runs the installation in qemu (if requested).
function prepare-installer { # 1: diskPaths function prepare-installer { # 1: diskPaths
run-hook-script 'Prepare Installer' @{config.installer.commands.prepareInstaller!writeText.prepareInstallerCommands} || exit
if [[ ! ${args[disks]:-} ]] ; then args[disks]=${1:?"The disks flag or the first positional argument must specify the path(s) to the disk(s) and/or image file(s) to install to"} ; shift ; fi if [[ ! ${args[disks]:-} ]] ; then args[disks]=${1:?"The disks flag or the first positional argument must specify the path(s) to the disk(s) and/or image file(s) to install to"} ; shift ; fi
umask g-w,o-w # Ensure that files created without explicit permissions are not writable for group and other. umask g-w,o-w # Ensure that files created without explicit permissions are not writable for group and other.

View File

@ -7,10 +7,7 @@ function prompt-for-user-passwords { # (void)
userPasswords[$user]=@{config.users.users!catAttrSets.password[$user]} userPasswords[$user]=@{config.users.users!catAttrSets.password[$user]}
done done
local user ; for user in "@{!config.users.users!catAttrSets.hashedPasswordFile[@]}" "@{!config.users.users!catAttrSets.passwordFile[@]}" ; do local user ; for user in "@{!config.users.users!catAttrSets.hashedPasswordFile[@]}" "@{!config.users.users!catAttrSets.passwordFile[@]}" ; do
for attempt in 2 3 x ; do prompt-new-password-thrice "for the user account »$user«"
if userPasswords[$user]=$(prompt-new-password "for the user account »$user«") ; then break ; fi
if [[ $attempt == x ]] ; then \return 1 ; fi ; echo "Retrying ($attempt/3):"
done
done done
} }

View File

@ -68,7 +68,7 @@ Example 2 (connect many VMs, unprivileged):
$ nix shell nixpkgs#vde2 --command vde_switch -sock /tmp/vm-net $ nix shell nixpkgs#vde2 --command vde_switch -sock /tmp/vm-net
$ ... --nic=vde,sock=/tmp/vm-net # multiple times" $ ... --nic=vde,sock=/tmp/vm-net # multiple times"
declare-flag run-qemu no-serial "" "Do not connect the calling terminal to a serial adapter the guest can log to and open a terminal on the guests serial, as would be the default if the guests logs to ttyS0." declare-flag run-qemu no-serial "" "Do not connect the calling terminal to a serial adapter the guest can log to and open a terminal on the guests serial, as would be the default if the guests logs to ttyS0."
declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM hte share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«." declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options«. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM the share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«."
declare-flag run-qemu virtio-blk "" "Pass the system's disks/images as virtio disks, instead of using AHCI+IDE. Default iff »boot.initrd.availableKernelModules« includes »virtio_blk« (because it requires that driver)." declare-flag run-qemu virtio-blk "" "Pass the system's disks/images as virtio disks, instead of using AHCI+IDE. Default iff »boot.initrd.availableKernelModules« includes »virtio_blk« (because it requires that driver)."
function run-qemu { function run-qemu {
if [[ ${args[install]:-} && ! ${argv[0]:-} ]] ; then argv[0]=/tmp/nixos-vm/@{config.installer.outputName:-@{config.system.name}}/ ; fi if [[ ${args[install]:-} && ! ${argv[0]:-} ]] ; then argv[0]=/tmp/nixos-vm/@{config.installer.outputName:-@{config.system.name}}/ ; fi
@ -176,7 +176,7 @@ declare-flag run-qemu,install-system,'*' vm-mem "num" "VM RAM in MiB (»
declare-flag run-qemu,install-system,'*' vm-smp "num" "Number of guest CPU cores." declare-flag run-qemu,install-system,'*' vm-smp "num" "Number of guest CPU cores."
declare-flag run-qemu,install-system,'*' vm-usb-port "path" "A physical USB port (or hub) to pass to the guest (e.g. a YubiKey for unlocking). Specified as »<bus>-<port>«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/<bus>-<port>« (see »lsusb -tvv«). E.g.: »--vm-usb-port=3-1.1.1.4«." declare-flag run-qemu,install-system,'*' vm-usb-port "path" "A physical USB port (or hub) to pass to the guest (e.g. a YubiKey for unlocking). Specified as »<bus>-<port>«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/<bus>-<port>« (see »lsusb -tvv«). E.g.: »--vm-usb-port=3-1.1.1.4«."
function apply-vm-args { function apply-vm-args {
qemu+=( -m ${args[vm-mem]:-2048} ) qemu+=( -m ${args[vm-mem]:-4096} )
if [[ ${args[vm-smp]:-} ]] ; then qemu+=( -smp ${args[vm-smp]} ) ; fi if [[ ${args[vm-smp]:-} ]] ; then qemu+=( -smp ${args[vm-smp]} ) ; fi
if [[ ${args[vm-usb-port]:-} ]] ; then local decl ; for decl in ${args[vm-usb-port]//:/ } ; do if [[ ${args[vm-usb-port]:-} ]] ; then local decl ; for decl in ${args[vm-usb-port]//:/ } ; do

View File

@ -45,6 +45,12 @@ function prompt-new-password {( set -u # 1: usage
if [[ "$password1" != "$password2" ]] ; then printf 'Passwords mismatch.\n' 1>&2 ; \exit 1 ; fi if [[ "$password1" != "$password2" ]] ; then printf 'Passwords mismatch.\n' 1>&2 ; \exit 1 ; fi
printf %s "$password1" || exit printf %s "$password1" || exit
)} )}
function prompt-new-password-thrice {
local attempt ; for attempt in 2 3 x ; do
if userPasswords[$user]=$( prompt-new-password "$@" ) ; then break ; fi
if [[ $attempt == x ]] ; then \return 1 ; fi ; echo "Retrying ($attempt/3):"
done
}
## If »secretFile« does not exist, interactively prompts up to three times for the secret to be stored in that file. ## If »secretFile« does not exist, interactively prompts up to three times for the secret to be stored in that file.
declare-flag '*' no-optional-prompts "" "Skip prompting for (and thus saving) secret marked as optional." declare-flag '*' no-optional-prompts "" "Skip prompting for (and thus saving) secret marked as optional."
@ -67,16 +73,15 @@ function prompt-secret-as {( set -u # 1: what, 2: secretFile, 3?: owner[:[group]
declare-flag install-system inspectScripts "" "When running installation hooks (»...*Commands« composed as Nix strings) print out and pause before each command. This works ... semi-well." declare-flag install-system inspectScripts "" "When running installation hooks (»...*Commands« composed as Nix strings) print out and pause before each command. This works ... semi-well."
## Runs an installer hook script, optionally stepping through the script. ## Runs an installer hook script, optionally stepping through the script.
function run-hook-script {( # 1: title, 2: scriptPath function run-hook-script { # 1: title, 2: scriptPath
trap - EXIT # start with empty traps for sub-shell
if [[ ${args[inspectScripts]:-} && "$(cat "$2")" != $'' ]] ; then if [[ ${args[inspectScripts]:-} && "$(cat "$2")" != $'' ]] ; then
echo "Running $1 commands. For each command printed, press Enter to continue or Ctrl+C to abort the installation:" 1>&2 echo "Running $1 commands. For each command printed, press Enter to continue or Ctrl+C to abort the installation:" 1>&2
# (this does not help against intentionally malicious scripts, it's quite easy to trick this) # (this does not help against intentionally malicious scripts, it's quite easy to trick this)
BASH_PREV_COMMAND= ; set -o functrace ; trap 'if [[ $BASH_COMMAND != "$BASH_PREV_COMMAND" ]] ; then echo -n "> $BASH_COMMAND" >&2 ; read ; fi ; BASH_PREV_COMMAND=$BASH_COMMAND' debug BASH_PREV_COMMAND= ; set -o functrace ; trap 'if [[ $BASH_COMMAND != "$BASH_PREV_COMMAND" ]] ; then echo -n "> $BASH_COMMAND" >&2 ; read ; fi ; BASH_PREV_COMMAND=$BASH_COMMAND' debug
fi fi
set -e # The called script snippets should not rely on this, but neither should this function rely on the scripts correctly exiting on errors.
source "$2" source "$2"
)} set +o functrace ; trap - debug
}
## Lazily builds a nix derivation at run time, instead of when building the script. ## Lazily builds a nix derivation at run time, instead of when building the script.
# When maybe-using packages that take long to build, instead of »at{some.package.out}«, use: »$( build-lazy at{some.package.drvPath!unsafeDiscardStringContext} out )« # When maybe-using packages that take long to build, instead of »at{some.package.out}«, use: »$( build-lazy at{some.package.drvPath!unsafeDiscardStringContext} out )«

View File

@ -36,6 +36,7 @@ in {
Note that these commands are executed without any further sandboxing (i.e. when not using the VM installation mode, as root on the host). Note that these commands are executed without any further sandboxing (i.e. when not using the VM installation mode, as root on the host).
Partitions may be used via `/dev/disk/by-partlabel/`.${lib.optionalString mounted '' The target system is mounted at `$mnt`.''} Partitions may be used via `/dev/disk/by-partlabel/`.${lib.optionalString mounted '' The target system is mounted at `$mnt`.''}
''; type = lib.types.lines; default = ""; }; in { ''; type = lib.types.lines; default = ""; }; in {
prepareInstaller = cmdOpt "early during preparation of the installer (right after CLI parsing)" false;
postPartition = cmdOpt "after partitioning the disks" false; postPartition = cmdOpt "after partitioning the disks" false;
postFormat = cmdOpt "after formatting the partitions with filesystems" false; postFormat = cmdOpt "after formatting the partitions with filesystems" false;
postMount = cmdOpt "after mounting all filesystems" true; postMount = cmdOpt "after mounting all filesystems" true;
@ -58,7 +59,7 @@ in {
config = { config = {
${installer} = { ${installer} = {
scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; }) (lib.self.setup-scripts); scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; order = 750; }) (lib.self.setup-scripts);
}; };
}; };

View File

@ -206,13 +206,11 @@ in {
}) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot. }) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot.
environment.etc = {
# SSHd host keys: # SSHd host keys:
"ssh/ssh_host_ed25519_key".source = "/${keep}/etc/ssh/ssh_host_ed25519_key"; environment.etc = lib.fun.mapMerge ({ path, ... }: if lib.hasPrefix "/etc/" path then let rel = lib.removePrefix "/etc/" path; in {
"ssh/ssh_host_ed25519_key.pub".source = "/${keep}/etc/ssh/ssh_host_ed25519_key.pub"; "${rel}".source = lib.mkDefault "/${keep}/etc/${rel}";
"ssh/ssh_host_rsa_key".source = "/${keep}/etc/ssh/ssh_host_rsa_key"; "${rel}.pub".source = lib.mkDefault "/${keep}/etc/${rel}.pub";
"ssh/ssh_host_rsa_key.pub".source = "/${keep}/etc/ssh/ssh_host_rsa_key.pub"; } else { }) (config.services.openssh.hostKeys);
};
systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ... systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ...
# »/root/.nix-channels« is already being restored. # »/root/.nix-channels« is already being restored.