at 24.11-pre 5.1 kB view raw
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. 4# 5# See: https://github.com/NixOS/rfcs/pull/125 6{ config 7, pkgs 8, lib 9, ... 10}: 11let 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 })); 35 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. 40 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}"; 54 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}"; 71 72 validator = pkgs.writeCueValidator ./bootspec.cue { 73 document = "Document"; # Universal validator for any version as long the schema is correctly set. 74 }; 75 }; 76 }; 77in 78{ 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 ''; 86 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. 93 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 }; 99 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 }; 108 109 validator = lib.mkOption { 110 internal = true; 111 default = schemas.v1.validator; 112 }; 113 114 filename = lib.mkOption { 115 internal = true; 116 default = schemas.v1.filename; 117 }; 118 }; 119}