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