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