diff --git a/modules/build.nix b/modules/build.nix index acfe30a..e6c096f 100644 --- a/modules/build.nix +++ b/modules/build.nix @@ -1,7 +1,7 @@ { lib, extendModules, modulesPath, baseModules, options, config, ... }: let - inherit (lib) recursiveUpdate mkOption; - inherit (lib.my) mkBoolOpt'; + inherit (lib) recursiveUpdate mkOption mkDefault; + inherit (lib.my) mkBoolOpt' dummyOption; cfg = config.my.build; @@ -17,19 +17,31 @@ let }; in { - options.my = with lib.types; { - boot.isDevVM = mkBoolOpt' false "Whether the system is a development VM."; - build = options.system.build; - asDevVM = mkOption { - inherit (asDevVM) type; - default = { }; - visible = "shallow"; - description = "Configuration as a development VM"; + options = with lib.types; { + my = { + boot.isDevVM = mkBoolOpt' false "Whether the system is a development VM."; + build = options.system.build; + asDevVM = mkOption { + inherit (asDevVM) type; + default = { }; + visible = "shallow"; + description = "Configuration as a development VM"; + }; + }; + + # Forward declare options that won't exist until the VM module is actually imported + virtualisation = { + diskImage = dummyOption; }; }; - config.my.build = { - # The meta.mainProgram should probably be set upstream but oh well... - devVM = recursiveUpdate config.my.asDevVM.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; }; + config = { + virtualisation = { + diskImage = mkDefault "./.vms/${config.system.name}.qcow2"; + }; + my.build = { + # The meta.mainProgram should probably be set upstream but oh well... + devVM = recursiveUpdate config.my.asDevVM.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; }; + }; }; } diff --git a/modules/tmproot.nix b/modules/tmproot.nix index fe5b678..a875132 100644 --- a/modules/tmproot.nix +++ b/modules/tmproot.nix @@ -1,9 +1,10 @@ { lib, pkgs, config, ... }: let - inherit (lib) concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkVMOverride; - inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride' dummyOption; + inherit (lib) optionalString concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkVMOverride; + inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride'; cfg = config.my.tmproot; + enablePersistence = cfg.persistDir != null; showUnsaved = '' @@ -56,27 +57,18 @@ in options = with lib.types; { my.tmproot = { enable = mkBoolOpt' true "Whether to enable tmproot."; - persistDir = mkOpt' str "/persist" "Path where persisted files are stored."; + persistDir = mkOpt' (nullOr str) "/persist" "Path where persisted files are stored."; size = mkOpt' str "2G" "Size of tmpfs root"; unsaved = { showMotd = mkBoolOpt' true "Whether to show unsaved files with `dynamic-motd`."; ignore = mkOpt' (listOf str) [ ] "Path prefixes to ignore if unsaved."; }; }; - - # Forward declare options that won't exist until the VM module is actually imported - virtualisation = { - diskImage = dummyOption; - }; }; config = mkIf cfg.enable (mkMerge [ { assertions = [ - { - assertion = config.fileSystems ? "${cfg.persistDir}"; - message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir})."; - } { # I mean you probably _could_, but if you're doing tmproot... come on assertion = !config.users.mutableUsers; @@ -112,48 +104,6 @@ in (pkgs.writeScriptBin "tmproot-unsaved" showUnsaved) ]; - # Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in - # nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang. - boot.initrd.postDeviceCommands = - '' - ensurePersistSource() { - [ -e "/mnt-root$1" ] && return - echo "Persistent source directory $1 does not exist, creating..." - install -dm "$2" "/mnt-root$1" || fail - } - - _waitDevice() { - local device="$1" - - ${concatMapStringsSep " || \\\n " (d: - let - sourceDir = "${d.persistentStoragePath}${d.directory}"; - in - ''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'') - config.environment.persistence."${cfg.persistDir}".directories} - - waitDevice "$@" - } - - type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail) - alias waitDevice=_waitDevice - ''; - - environment.persistence."${cfg.persistDir}" = { - hideMounts = mkDefault true; - directories = [ - "/var/log" - # In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted - # users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update". - # Also the script runs before impermanence does. - "/var/lib/nixos" - "/var/lib/systemd" - ]; - files = [ - "/etc/machine-id" - ]; - }; - my.dynamic-motd.script = mkIf cfg.unsaved.showMotd '' tmprootUnsaved() { @@ -163,8 +113,10 @@ in echo echo -e "\t\e[31;1;4mWarning:\e[0m $count file(s) on / will be lost on shutdown!" echo -e '\tTo see them, run `tmproot-unsaved` as root.' - echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!' - echo -e '\tOtherwise, they can be ignored by adding to `my.tmproot.unsaved.ignore`.' + ${optionalString enablePersistence '' + echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!' + ''} + echo -e "\tIf they don't need to be kept, add them to \`my.tmproot.unsaved.ignore\`." echo } @@ -172,15 +124,8 @@ in ''; fileSystems."/" = rootDef; - - virtualisation = { - diskImage = "./.vms/${config.system.name}-persist.qcow2"; - }; } - (mkIf config.services.openssh.enable { - environment.persistence."${cfg.persistDir}".files = - concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys; - }) + (mkIf config.networking.resolvconf.enable { my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ]; }) @@ -192,12 +137,77 @@ in fileSystems = mkVMOverride { "/" = mkVMOverride' rootDef; - # Hijack the "root" device for persistence in the VM - "${cfg.persistDir}" = { - device = config.virtualisation.bootDevice; - neededForBoot = true; - }; }; }) + + (mkIf enablePersistence (mkMerge [ + { + assertions = [ + { + assertion = config.fileSystems ? "${cfg.persistDir}"; + message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir})."; + } + ]; + + # Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in + # nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang. + boot.initrd.postDeviceCommands = + '' + ensurePersistSource() { + [ -e "/mnt-root$1" ] && return + echo "Persistent source directory $1 does not exist, creating..." + install -dm "$2" "/mnt-root$1" || fail + } + + _waitDevice() { + local device="$1" + + ${concatMapStringsSep " || \\\n " (d: + let + sourceDir = "${d.persistentStoragePath}${d.directory}"; + in + ''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'') + config.environment.persistence."${cfg.persistDir}".directories} + + waitDevice "$@" + } + + type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail) + alias waitDevice=_waitDevice + ''; + + environment.persistence."${cfg.persistDir}" = { + hideMounts = mkDefault true; + directories = [ + "/var/log" + # In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted + # users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update". + # Also the script runs before impermanence does. + "/var/lib/nixos" + "/var/lib/systemd" + ]; + files = [ + "/etc/machine-id" + ]; + }; + + virtualisation = { + diskImage = "./.vms/${config.system.name}-persist.qcow2"; + }; + } + (mkIf config.services.openssh.enable { + environment.persistence."${cfg.persistDir}".files = + concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys; + }) + (mkIf config.my.boot.isDevVM { + fileSystems = mkVMOverride { + # Hijack the "root" device for persistence in the VM + "${cfg.persistDir}" = { + device = config.virtualisation.bootDevice; + neededForBoot = true; + }; + }; + }) + ])) ]); } diff --git a/systems.nix b/systems.nix index ec5ba11..15c1660 100644 --- a/systems.nix +++ b/systems.nix @@ -1,13 +1,14 @@ { lib, pkgsFlakes, inputs, modules }: let inherit (builtins) attrValues mapAttrs; - inherit (lib) mkDefault; + inherit (lib) optionals mkDefault; mkSystem = name: { system, nixpkgs ? "unstable", config, + docCustom ? true, }: let pkgsFlake = pkgsFlakes.${nixpkgs}; @@ -31,8 +32,8 @@ let inputs.impermanence.nixosModule inputs.agenix.nixosModules.age inputs.home-manager.nixosModule - ] ++ modules; - modules = [ + ] ++ (optionals docCustom modules); + modules = (optionals (!docCustom) modules) ++ [ { _module.args = { inherit system inputs; }; system.name = name;