at 25.11-pre 5.6 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{ 7 config, 8 pkgs, 9 lib, 10 ... 11}: 12let 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 ); 43 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. 48 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}"; 68 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}"; 93 94 validator = pkgs.writeCueValidator ./bootspec.cue { 95 document = "Document"; # Universal validator for any version as long the schema is correctly set. 96 }; 97 }; 98 }; 99in 100{ 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 ''; 113 114 package = lib.mkPackageOption pkgs "bootspec" { }; 115 116 extensions = lib.mkOption { 117 # NOTE(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation. 118 type = lib.types.attrsOf lib.types.anything; # <namespace>: { ...namespace-specific fields } 119 default = { }; 120 description = '' 121 User-defined data that extends the bootspec document. 122 123 To reduce incompatibility and prevent names from clashing 124 between applications, it is **highly recommended** to use a 125 unique namespace for your extensions. 126 ''; 127 }; 128 129 # This will be run as a part of the `systemBuilder` in ./top-level.nix. This 130 # means `$out` points to the output of `config.system.build.toplevel` and can 131 # be used for a variety of things (though, for now, it's only used to report 132 # the path of the `toplevel` itself and the `init` executable). 133 writer = lib.mkOption { 134 internal = true; 135 default = schemas.v1.generator; 136 }; 137 138 validator = lib.mkOption { 139 internal = true; 140 default = schemas.v1.validator; 141 }; 142 143 filename = lib.mkOption { 144 internal = true; 145 default = schemas.v1.filename; 146 }; 147 }; 148}