base16-schemes: unstable-2024-06-21 -> unstable-2024-11-12
[NixPkgs.git] / nixos / modules / system / activation / bootspec.nix
blob3c2b91cce70162be907d74493cf8db79e7ed1782
1 # Note that these schemas are defined by RFC-0125.
2 # This document is considered a stable API, and is depended upon by external tooling.
3 # Changes to the structure of the document, or the semantics of the values should go through an RFC.
5 # See: https://github.com/NixOS/rfcs/pull/125
6 { config
7 , pkgs
8 , lib
9 , ...
11 let
12   cfg = config.boot.bootspec;
13   children = lib.mapAttrs (childName: childConfig: childConfig.configuration.system.build.toplevel) config.specialisation;
14   hasAtLeastOneInitrdSecret = lib.length (lib.attrNames config.boot.initrd.secrets) > 0;
15   schemas = {
16     v1 = rec {
17       filename = "boot.json";
18       json =
19         pkgs.writeText filename
20         (builtins.toJSON
21           # Merge extensions first to not let them shadow NixOS bootspec data.
22           (cfg.extensions //
23           {
24             "org.nixos.bootspec.v1" = {
25               system = config.boot.kernelPackages.stdenv.hostPlatform.system;
26               kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
27               kernelParams = config.boot.kernelParams;
28               label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})";
29             } // lib.optionalAttrs config.boot.initrd.enable {
30               initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
31             } // lib.optionalAttrs hasAtLeastOneInitrdSecret {
32               initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets";
33             };
34           }));
36       generator =
37         let
38           # NOTE: Be careful to not introduce excess newlines at the end of the
39           # injectors, as that may affect the pipes and redirects.
41           # Inject toplevel and init into the bootspec.
42           # This can only be done here because we *cannot* depend on $out
43           # referring to the toplevel, except by living in the toplevel itself.
44           toplevelInjector = lib.escapeShellArgs [
45             "${pkgs.buildPackages.jq}/bin/jq"
46             ''
47               ."org.nixos.bootspec.v1".toplevel = $toplevel |
48               ."org.nixos.bootspec.v1".init = $init
49             ''
50             "--sort-keys"
51             "--arg" "toplevel" "${placeholder "out"}"
52             "--arg" "init" "${placeholder "out"}/init"
53           ] + " < ${json}";
55           # We slurp all specialisations and inject them as values, such that
56           # `.specialisations.${name}` embeds the specialisation's bootspec
57           # document.
58           specialisationInjector =
59             let
60               specialisationLoader = (lib.mapAttrsToList
61                 (childName: childToplevel: lib.escapeShellArgs [ "--slurpfile" childName "${childToplevel}/${filename}" ])
62                 children);
63             in
64             lib.escapeShellArgs [
65               "${pkgs.buildPackages.jq}/bin/jq"
66               "--sort-keys"
67               ''."org.nixos.specialisation.v1" = ($ARGS.named | map_values(. | first))''
68             ] + " ${lib.concatStringsSep " " specialisationLoader}";
69         in
70         "${toplevelInjector} | ${specialisationInjector} > $out/${filename}";
72       validator = pkgs.writeCueValidator ./bootspec.cue {
73         document = "Document"; # Universal validator for any version as long the schema is correctly set.
74       };
75     };
76   };
79   options.boot.bootspec = {
80     enable = lib.mkEnableOption "the generation of RFC-0125 bootspec in $system/boot.json, e.g. /run/current-system/boot.json"
81       // { default = true; internal = true; };
82     enableValidation = lib.mkEnableOption ''the validation of bootspec documents for each build.
83       This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation.
84       Enable this option if you want to ascertain that your documents are correct
85       '';
87     extensions = lib.mkOption {
88       # NOTE(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation.
89       type = lib.types.attrsOf lib.types.anything; # <namespace>: { ...namespace-specific fields }
90       default = { };
91       description = ''
92         User-defined data that extends the bootspec document.
94         To reduce incompatibility and prevent names from clashing
95         between applications, it is **highly recommended** to use a
96         unique namespace for your extensions.
97       '';
98     };
100     # This will be run as a part of the `systemBuilder` in ./top-level.nix. This
101     # means `$out` points to the output of `config.system.build.toplevel` and can
102     # be used for a variety of things (though, for now, it's only used to report
103     # the path of the `toplevel` itself and the `init` executable).
104     writer = lib.mkOption {
105       internal = true;
106       default = schemas.v1.generator;
107     };
109     validator = lib.mkOption {
110       internal = true;
111       default = schemas.v1.validator;
112     };
114     filename = lib.mkOption {
115       internal = true;
116       default = schemas.v1.filename;
117     };
118   };