nixosModules.pkgsReadOnly: init

Changed files
+146
nixos
modules
misc
+13
flake.nix
···
nixosModules = {
notDetected = ./nixos/modules/installer/scan/not-detected.nix;
+
+
/*
+
Make the `nixpkgs.*` configuration read-only. Guarantees that `pkgs`
+
is the way you initialize it.
+
+
Example:
+
+
{
+
imports = [ nixpkgs.nixosModules.readOnlyPkgs ];
+
nixpkgs.pkgs = nixpkgs.legacyPackages.x86_64-linux;
+
}
+
*/
+
readOnlyPkgs = ./nixos/modules/misc/nixpkgs/read-only.nix;
};
};
}
+74
nixos/modules/misc/nixpkgs/read-only.nix
···
+
# A replacement for the traditional nixpkgs module, such that none of the modules
+
# can add their own configuration. This ensures that the Nixpkgs configuration is
+
# exactly as the user intends.
+
# This may also be used as a performance optimization when evaluating multiple
+
# configurations at once, with a shared `pkgs`.
+
+
# This is a separate module, because merging this logic into the nixpkgs module
+
# is too burdensome, considering that it is already burdened with legacy.
+
# Moving this logic into a module does not lose any composition benefits, because
+
# its purpose is not something that composes anyway.
+
+
{ lib, config, ... }:
+
+
let
+
cfg = config.nixpkgs;
+
inherit (lib) mkOption types;
+
+
in
+
{
+
disabledModules = [
+
../nixpkgs.nix
+
];
+
options = {
+
nixpkgs = {
+
pkgs = mkOption {
+
type = lib.types.pkgs;
+
description = lib.mdDoc ''The pkgs module argument.'';
+
};
+
config = mkOption {
+
internal = true;
+
type = types.unique { message = "nixpkgs.config is set to read-only"; } types.anything;
+
description = lib.mdDoc ''
+
The Nixpkgs `config` that `pkgs` was initialized with.
+
'';
+
};
+
overlays = mkOption {
+
internal = true;
+
type = types.unique { message = "nixpkgs.overlays is set to read-only"; } types.anything;
+
description = lib.mdDoc ''
+
The Nixpkgs overlays that `pkgs` was initialized with.
+
'';
+
};
+
hostPlatform = mkOption {
+
internal = true;
+
readOnly = true;
+
description = lib.mdDoc ''
+
The platform of the machine that is running the NixOS configuration.
+
'';
+
};
+
buildPlatform = mkOption {
+
internal = true;
+
readOnly = true;
+
description = lib.mdDoc ''
+
The platform of the machine that built the NixOS configuration.
+
'';
+
};
+
# NOTE: do not add the legacy options such as localSystem here. Let's keep
+
# this module simple and let module authors upgrade their code instead.
+
};
+
};
+
config = {
+
_module.args.pkgs =
+
# find mistaken definitions
+
builtins.seq cfg.config
+
builtins.seq cfg.overlays
+
builtins.seq cfg.hostPlatform
+
builtins.seq cfg.buildPlatform
+
cfg.pkgs;
+
nixpkgs.config = cfg.pkgs.config;
+
nixpkgs.overlays = cfg.pkgs.overlays;
+
nixpkgs.hostPlatform = cfg.pkgs.stdenv.hostPlatform;
+
nixpkgs.buildPlatform = cfg.pkgs.stdenv.buildPlatform;
+
};
+
}
+59
nixos/modules/misc/nixpkgs/test.nix
···
+
# [nixpkgs]$ nix-build -A nixosTests.nixpkgs --show-trace
+
{ evalMinimalConfig, pkgs, lib, stdenv }:
let
eval = mod: evalMinimalConfig {
···
let
uncheckedEval = lib.evalModules { modules = [ ../nixpkgs.nix module ]; };
in map (ass: ass.message) (lib.filter (ass: !ass.assertion) uncheckedEval.config.assertions);
+
+
readOnlyUndefined = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
};
+
+
readOnlyBad = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = { };
+
};
+
+
readOnly = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = pkgs;
+
};
+
+
readOnlyBadConfig = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = pkgs;
+
nixpkgs.config.allowUnfree = true; # do in pkgs instead!
+
};
+
+
readOnlyBadOverlays = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = pkgs;
+
nixpkgs.overlays = [ (_: _: {}) ]; # do in pkgs instead!
+
};
+
+
readOnlyBadHostPlatform = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = pkgs;
+
nixpkgs.hostPlatform = "foo-linux"; # do in pkgs instead!
+
};
+
+
readOnlyBadBuildPlatform = evalMinimalConfig {
+
imports = [ ./read-only.nix ];
+
nixpkgs.pkgs = pkgs;
+
nixpkgs.buildPlatform = "foo-linux"; # do in pkgs instead!
+
};
+
+
throws = x: ! (builtins.tryEval x).success;
+
in
lib.recurseIntoAttrs {
invokeNixpkgsSimple =
···
nixpkgs.hostPlatform = pkgs.stdenv.hostPlatform;
nixpkgs.pkgs = pkgs;
} == [];
+
+
+
# Tests for the read-only.nix module
+
assert readOnly._module.args.pkgs.stdenv.hostPlatform.system == pkgs.stdenv.hostPlatform.system;
+
assert throws readOnlyBad._module.args.pkgs.stdenv;
+
assert throws readOnlyUndefined._module.args.pkgs.stdenv;
+
assert throws readOnlyBadConfig._module.args.pkgs.stdenv;
+
assert throws readOnlyBadOverlays._module.args.pkgs.stdenv;
+
assert throws readOnlyBadHostPlatform._module.args.pkgs.stdenv;
+
assert throws readOnlyBadBuildPlatform._module.args.pkgs.stdenv;
+
# read-only.nix does not provide legacy options, for the sake of simplicity
+
# If you're bothered by this, upgrade your configs to use the new *Platform
+
# options.
+
assert !readOnly.options.nixpkgs?system;
+
assert !readOnly.options.nixpkgs?localSystem;
+
assert !readOnly.options.nixpkgs?crossSystem;
pkgs.emptyFile;
}