nixpkgs/nixos/modules/system/activation/bootspec.nix
Silvan Mosberger 4f0dadbf38 treewide: format all inactive Nix files
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.

Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.

A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.

This commit was automatically created and can be verified using

    nix-build a08b3a4d19.tar.gz \
      --argstr baseRev b32a094368
    result/bin/apply-formatting $NIXPKGS_PATH
2024-12-10 20:26:33 +01:00

147 lines
5.4 KiB
Nix

# Note that these schemas are defined by RFC-0125.
# This document is considered a stable API, and is depended upon by external tooling.
# Changes to the structure of the document, or the semantics of the values should go through an RFC.
#
# See: https://github.com/NixOS/rfcs/pull/125
{
config,
pkgs,
lib,
...
}:
let
cfg = config.boot.bootspec;
children = lib.mapAttrs (
childName: childConfig: childConfig.configuration.system.build.toplevel
) config.specialisation;
hasAtLeastOneInitrdSecret = lib.length (lib.attrNames config.boot.initrd.secrets) > 0;
schemas = {
v1 = rec {
filename = "boot.json";
json = pkgs.writeText filename (
builtins.toJSON
# Merge extensions first to not let them shadow NixOS bootspec data.
(
cfg.extensions
// {
"org.nixos.bootspec.v1" =
{
system = config.boot.kernelPackages.stdenv.hostPlatform.system;
kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
kernelParams = config.boot.kernelParams;
label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})";
}
// lib.optionalAttrs config.boot.initrd.enable {
initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
}
// lib.optionalAttrs hasAtLeastOneInitrdSecret {
initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets";
};
}
)
);
generator =
let
# NOTE: Be careful to not introduce excess newlines at the end of the
# injectors, as that may affect the pipes and redirects.
# Inject toplevel and init into the bootspec.
# This can only be done here because we *cannot* depend on $out
# referring to the toplevel, except by living in the toplevel itself.
toplevelInjector =
lib.escapeShellArgs [
"${pkgs.buildPackages.jq}/bin/jq"
''
."org.nixos.bootspec.v1".toplevel = $toplevel |
."org.nixos.bootspec.v1".init = $init
''
"--sort-keys"
"--arg"
"toplevel"
"${placeholder "out"}"
"--arg"
"init"
"${placeholder "out"}/init"
]
+ " < ${json}";
# We slurp all specialisations and inject them as values, such that
# `.specialisations.${name}` embeds the specialisation's bootspec
# document.
specialisationInjector =
let
specialisationLoader = (
lib.mapAttrsToList (
childName: childToplevel:
lib.escapeShellArgs [
"--slurpfile"
childName
"${childToplevel}/${filename}"
]
) children
);
in
lib.escapeShellArgs [
"${pkgs.buildPackages.jq}/bin/jq"
"--sort-keys"
''."org.nixos.specialisation.v1" = ($ARGS.named | map_values(. | first))''
]
+ " ${lib.concatStringsSep " " specialisationLoader}";
in
"${toplevelInjector} | ${specialisationInjector} > $out/${filename}";
validator = pkgs.writeCueValidator ./bootspec.cue {
document = "Document"; # Universal validator for any version as long the schema is correctly set.
};
};
};
in
{
options.boot.bootspec = {
enable =
lib.mkEnableOption "the generation of RFC-0125 bootspec in $system/boot.json, e.g. /run/current-system/boot.json"
// {
default = true;
internal = true;
};
enableValidation = lib.mkEnableOption ''
the validation of bootspec documents for each build.
This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation.
Enable this option if you want to ascertain that your documents are correct
'';
extensions = lib.mkOption {
# NOTE(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation.
type = lib.types.attrsOf lib.types.anything; # <namespace>: { ...namespace-specific fields }
default = { };
description = ''
User-defined data that extends the bootspec document.
To reduce incompatibility and prevent names from clashing
between applications, it is **highly recommended** to use a
unique namespace for your extensions.
'';
};
# This will be run as a part of the `systemBuilder` in ./top-level.nix. This
# means `$out` points to the output of `config.system.build.toplevel` and can
# be used for a variety of things (though, for now, it's only used to report
# the path of the `toplevel` itself and the `init` executable).
writer = lib.mkOption {
internal = true;
default = schemas.v1.generator;
};
validator = lib.mkOption {
internal = true;
default = schemas.v1.validator;
};
filename = lib.mkOption {
internal = true;
default = schemas.v1.filename;
};
};
}