From 9ef807877e31b7525951800df50be5494a8a7768 Mon Sep 17 00:00:00 2001 From: Niklas Gollenstede Date: Wed, 18 May 2022 16:06:27 +0200 Subject: [PATCH] =?UTF-8?q?fix=20typo=20in=20=C2=BBmkSystemsFlake=C2=AB,?= =?UTF-8?q?=20make=20system's=20$out/config=20optional,=20don't=20use=20pa?= =?UTF-8?q?tches=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/extensions.json | 6 +++++ .vscode/settings.json | 1 + flake.nix | 6 ++--- lib/flakes.nix | 47 ++++++++++++++++++------------------- lib/imports.nix | 20 ++++++++-------- lib/misc.nix | 2 +- lib/setup-scripts/README.md | 2 +- modules/base.nix.md | 5 +++- 8 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..864c85c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "jnoortheen.nix-ide", + "streetsidesoftware.code-spell-checker", + ], +} diff --git a/.vscode/settings.json b/.vscode/settings.json index d7da54e..ce3e872 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -51,6 +51,7 @@ "mountpoint", // program / function "namespacing", // word "netbootxyz", // option + "niklas", // name "nixos", // (duh) "nixpkgs", // nix "noatime", // mount option diff --git a/flake.nix b/flake.nix index 2589f7d..9d184e8 100644 --- a/flake.nix +++ b/flake.nix @@ -13,13 +13,13 @@ }; outputs = inputs: let patches = { nixpkgs = [ - ./patches/nixpkgs-test.patch # after »nix build«, check »result/inputs/nixpkgs/patched!« to see that these patches were applied - ./patches/nixpkgs-fix-systemd-boot-install.patch + # ./patches/nixpkgs-test.patch # after »nix build«, check »result/inputs/nixpkgs/patched!« to see that these patches were applied + # ./patches/nixpkgs-fix-systemd-boot-install.patch ]; }; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@ { self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let - systemsFlake = lib.wip.mkSystemsFalke (rec { + systemsFlake = lib.wip.mkSystemsFlake (rec { #systems = { dir = "${./.}/hosts"; exclude = [ ]; }; inherit inputs; scripts = [ ./example/install.sh.md ] ++ (lib.attrValues lib.wip.setup-scripts); diff --git a/lib/flakes.nix b/lib/flakes.nix index 2605676..d81322c 100644 --- a/lib/flakes.nix +++ b/lib/flakes.nix @@ -22,7 +22,7 @@ in rec { # A non-flake has only the attrs of »sourceInfo«. # A flake has »{ inputs; outputs; sourceInfo; } // outputs // sourceInfo«, where »inputs« is what's passed to the outputs function without »self«, and »outputs« is the result of calling the outputs function. Don't know the merge priority. if (!input?sourceInfo) then sourceInfo else (let - outputs = (import "${patched.outPath}/flake.nix").outputs ({ self = outputs; } // input.inputs); + outputs = (import "${patched.outPath}/flake.nix").outputs ({ self = sourceInfo // outputs; } // input.inputs); in { inherit (input) inputs; inherit outputs; inherit sourceInfo; } // outputs // sourceInfo) )) else input) inputs); @@ -58,11 +58,13 @@ in rec { ) then (props) else throw "File ${entryPath} must fulfill the structure: dirname: inputs: { ... }: { imports = [ { preface = { hardware = str; ... } } ]; }"; # Builds the System Configuration for a single host. Since each host depends on the context of all other host (in the same "network"), this is essentially only callable through »mkNixosConfigurations«. - # See »mkSystemsFalke« for documentation of the arguments. + # See »mkSystemsFlake« for documentation of the arguments. mkNixosConfiguration = args@{ name, entryPath, peers, inputs, overlays, modules, nixosSystem, localSystem ? null, ... }: let preface = (getSystemPreface inputs entryPath ({ inherit lib; } // specialArgs)); targetSystem = "${preface.hardware}-linux"; buildSystem = if localSystem != null then localSystem else targetSystem; - specialArgs = (args.specialArgs or { }) // { # make these available in the attrSet passed to the modules + specialArgs = { # make these available in the attrSet passed to the modules + inherit inputs; # These are global and passed by the caller of this function (or not), so avoid using these (in favor of the own flakes inputs) where possible! + } // (args.specialArgs or { }) // { inherit name; nodes = peers; # NixOPS }; in { inherit preface; } // (nixosSystem { @@ -84,8 +86,6 @@ in rec { system.extraSystemBuilderCmds = (if !config.boot.initrd.enable then "" else '' ln -sT ${builtins.unsafeDiscardStringContext config.system.build.bootStage1} $out/boot-stage-1.sh # (this is super annoying to locate otherwise) - '') + (if !inputs?self then "" else '' - ln -sT ${inputs.self.outPath} $out/config # (build input for reference) ''); }) ]; @@ -94,7 +94,7 @@ in rec { # 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 file 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 »specialArgs« to the modules and can thus be used to adjust the config per instance). - # All other arguments are as specified by »mkSystemsFalke« and are passed to »mkNixosConfiguration«. + # All other arguments are as specified by »mkSystemsFlake« and are passed to »mkNixosConfiguration«. mkNixosConfigurations = args: let # { files, dir, exclude, ... } files = args.files or (getNixFiles args.dir (args.exclude or [ ])); files' = if builtins.isAttrs files then files else (builtins.listToAttrs (map (entryPath: let @@ -121,7 +121,7 @@ in rec { # Builds a system of NixOS hosts and exports them plus managing functions as flake outputs. # All arguments are optional, as long as the default can be derived from the other arguments as passed. - mkSystemsFalke = args@{ + mkSystemsFlake = args@{ # An attrset of imported Nix flakes, for example the argument(s) passed to the flake »outputs« function. All other arguments are optional (and have reasonable defaults) if this is provided and contains »self« and the standard »nixpkgs«. This is also the second argument passed to the individual host's top level config files. inputs ? { }, # Root path of the NixOS configuration. »./.« in the »flake.nix« @@ -208,23 +208,22 @@ in rec { }) // { - packages.all-systems = pkgs.stdenv.mkDerivation { # dummy that just pulls in all system builds - name = "all-systems"; src = ./.; installPhase = '' - mkdir -p $out/systems - ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: ( - "ln -sT ${system.config.system.build.toplevel} $out/systems/${name}" - )) nixosConfigurations)} - ${lib.optionalString (scripts != [ ]) '' - mkdir -p $out/scripts - ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: _: "ln -sT ${outputs.apps.${localSystem}.${name}.program} $out/scripts/${name}") nixosConfigurations)} - ''} - ${lib.optionalString (inputs != { }) '' - mkdir -p $out/inputs - ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: { outPath, ... }: "ln -sT ${outPath} $out/inputs/${name}") inputs)} - ''} - ${lib.optionalString (configPath != null) "ln -sT ${configPath} $out/config"} - ''; - }; + # dummy that just pulls in all system builds + packages.all-systems = pkgs.runCommandLocal "all-systems" { } '' + mkdir -p $out/systems + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: ( + "ln -sT ${system.config.system.build.toplevel} $out/systems/${name}" + )) nixosConfigurations)} + ${lib.optionalString (scripts != [ ]) '' + mkdir -p $out/scripts + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: _: "ln -sT ${outputs.apps.${localSystem}.${name}.program} $out/scripts/${name}") nixosConfigurations)} + ''} + ${lib.optionalString (inputs != { }) '' + mkdir -p $out/inputs + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: { outPath, ... }: "ln -sT ${outPath} $out/inputs/${name}") inputs)} + ''} + ${lib.optionalString (configPath != null) "ln -sT ${configPath} $out/config"} + ''; })); in outputs; diff --git a/lib/imports.nix b/lib/imports.nix index 182349a..60fcdb0 100644 --- a/lib/imports.nix +++ b/lib/imports.nix @@ -74,14 +74,14 @@ in rec { names = builtins.concatLists (map (overlay: builtins.attrNames (overlay { } { })) (builtins.attrValues overlays)); in mapMerge (name: { ${name} = pkgs.${name}; }) names; - ## Given a path to a module in »nixpkgs/nixos/modules/« and placed in another module's »imports«, adds an option »disableModule.« that defaults to being false, but when explicitly set to »true«, disables all »config« values set by the module. + ## Given a path to a module in »nixpkgs/nixos/modules/«, when placed in another module's »imports«, this adds an option »disableModule.${modulePath}« that defaults to being false, but when explicitly set to »true«, disables all »config« values set by the module. # Every module should, but not all modules do, provide such an option themselves. # This is similar to adding the path to »disabledModules«, but: # * leaves the module's other definitions (options, imports) untouched, preventing further breakage due to missing options # * makes the disabling an option, i.e. it can be changed dynamically based on other config values - makeNixpkgsModuleConfigOptional = nixpkgs: specialArgs: modulePath: let - fullPath = "${nixpkgs.outPath}/nixos/modules/${modulePath}"; - moduleArgs = { utils = import "${nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs; + makeNixpkgsModuleConfigOptional = specialArgs: modulePath: let + fullPath = "${specialArgs.inputs.nixpkgs.outPath}/nixos/modules/${modulePath}"; + moduleArgs = { utils = import "${specialArgs.inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs; module = import fullPath moduleArgs; in { _file = fullPath; imports = [ { options.disableModule.${modulePath} = lib.mkOption { description = "Disable the nixpkgs module ${modulePath}"; type = lib.types.bool; default = false; }; } @@ -93,12 +93,12 @@ in rec { { disabledModules = [ modulePath ]; } ]; }; - ## Given a path to a module and a function taking the instantiation of the original and returning a partial module as override, recursively applies that override to the original module definition. - # This allows for much more fine-grained overriding of the configuration (or even other parts) of a module than »makeNixpkgsModuleConfigOptional«, but the override function needs to be tailored to internal implementation details of the original module. - # Esp. it is important to know that »mkIf« both existing in the original module and in the return from the override results in an attrset »{ _type="if"; condition; content; }«. Accessing content from an existing »mkIf« thus requires adding ».content« to the lookup path, and the »content« of returned »mkIf«s may get merged with any existing attribute of that name. - overrideNixpkgsModule = nixpkgs: specialArgs: modulePath: override: let - fullPath = "${nixpkgs.outPath}/nixos/modules/${modulePath}"; - moduleArgs = { utils = import "${nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs; + ## Given a path to a module, and a function that takes the instantiation of the original module and returns a partial module as override, this recursively applies that override to the original module definition. + # Used as an »imports« entry, this allows for much more fine-grained overriding of the configuration (or even other parts) of a module than »makeNixpkgsModuleConfigOptional«, but the override function needs to be tailored to internal implementation details of the original module. + # Esp. it is important to know that »mkIf« both existing in the original module and in the return from the override results in an attrset »{ _type="if"; condition; content; }«. Accessing content of an existing »mkIf« thus requires adding ».content« to the lookup path, and the »content« of returned »mkIf«s will get merged with any existing attribute of that name. + overrideNixpkgsModule = specialArgs: modulePath: override: let + fullPath = "${specialArgs.inputs.nixpkgs.outPath}/nixos/modules/${modulePath}"; + moduleArgs = { utils = import "${specialArgs.inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs; module = import fullPath moduleArgs; in { _file = fullPath; imports = [ (mergeAttrsRecursive [ { imports = module.imports or [ ]; options = module.options or { }; config = module.config or { }; } (override module) ]) diff --git a/lib/misc.nix b/lib/misc.nix index fbe4736..ffe0ec3 100644 --- a/lib/misc.nix +++ b/lib/misc.nix @@ -31,7 +31,7 @@ in rec { preferredRoute = from: to: (lib.findFirst ({ prefix, ip, ... }: prefix == "" || (builtins.any (fromSub: startsWith prefix fromSub) from)) { ip = ""; } to).ip; # Given a message and any value, traces both the message and the value, and returns the value. - trace = lib: message: value: (builtins.trace (message +": "+ (lib.generators.toPretty { } value)) value); + trace = message: value: (builtins.trace (message +": "+ (lib.generators.toPretty { } value)) value); rpoolOf = hostName: "rpool-${builtins.substring 0 8 (builtins.hashString "sha256" hostName)}"; diff --git a/lib/setup-scripts/README.md b/lib/setup-scripts/README.md index 63f430f..9820825 100644 --- a/lib/setup-scripts/README.md +++ b/lib/setup-scripts/README.md @@ -3,7 +3,7 @@ This is a library of bash functions, mostly for NixOS system installation. -The (paths to these) scripts are meant to me passed in the `scripts` argument to [`mkSystemsFalke`](../flakes.nix#mkSystemsFalke), which makes their functions available in the per-host `devShells`/`apps`. +The (paths to these) scripts are meant to me passed in the `scripts` argument to [`mkSystemsFlake`](../flakes.nix#mkSystemsFlake), which makes their functions available in the per-host `devShells`/`apps`. Host-specific nix variables are available to the bash functions as `@{...}` through [`substituteImplicit`](../scripts.nix#substituteImplicit) with the respective host as root context. With the functions from here, adding a simple three-liner can be enough to do a completely automated NixOS installation: diff --git a/modules/base.nix.md b/modules/base.nix.md index d72e4c6..ef67b2e 100644 --- a/modules/base.nix.md +++ b/modules/base.nix.md @@ -9,7 +9,7 @@ Things that really should be (more like) this by default. ```nix #*/# end of MarkDown, beginning of NixOS module: -dirname: inputs: { config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let +dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let prefix = inputs.config.prefix; cfg = config.${prefix}.base; in { @@ -45,6 +45,9 @@ in { nix.nixPath = [ "nixpkgs=/etc/nix/channels/nixpkgs" "nixos-config=/etc/nixos" ]; nix.extraOptions = "experimental-features = nix-command flakes"; # apparently, even nix 2.8 (in nixos-22.05) needs this environment.shellAliases = { "with" = ''nix-shell --run "bash --login" -p''; }; + system.extraSystemBuilderCmds = (if !specialArgs?inputs && !specialArgs.inputs?self then "" else '' + ln -sT ${specialArgs.inputs.self.outPath} $out/config # (build input for reference) + ''); }) ({