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