diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6526b8203..f8078c495 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -21,6 +21,9 @@
/modules/misc/news.nix @rycee
+/modules/misc/nix.nix @polykernel
+/tests/modules/misc/nix @polykernel
+
/modules/misc/nixpkgs-disabled.nix @thiagokokada
/modules/misc/numlock.nix @evanjs
diff --git a/modules/misc/nix.nix b/modules/misc/nix.nix
index a044400fa..e722c7d99 100644
--- a/modules/misc/nix.nix
+++ b/modules/misc/nix.nix
@@ -6,8 +6,98 @@ let
cfg = config.nix;
+ nixPackage = cfg.package;
+
+ isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+ nixConf = assert isNixAtLeast "2.2";
+ let
+
+ mkValueString = v:
+ if v == null then
+ ""
+ else if isInt v then
+ toString v
+ else if isBool v then
+ boolToString v
+ else if isFloat v then
+ floatToString v
+ else if isList v then
+ toString v
+ else if isDerivation v then
+ toString v
+ else if builtins.isPath v then
+ toString v
+ else if isString v then
+ v
+ else if isCoercibleToString v then
+ toString v
+ else
+ abort "The nix conf value: ${toPretty { } v} can not be encoded";
+
+ mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
+
+ mkKeyValuePairs = attrs:
+ concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
+
+ in pkgs.writeTextFile {
+ name = "nix.conf";
+ text = ''
+ # WARNING: this file is generated from the nix.settings option in
+ # your Home Manager configuration at $XDG_CONFIG_HOME/nix/nix.conf.
+ # Do not edit it!
+ ${mkKeyValuePairs cfg.settings}
+ ${cfg.extraOptions}
+ '';
+ checkPhase =
+ if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
+ echo "Ignoring validation for cross-compilation"
+ '' else ''
+ echo "Validating generated nix.conf"
+ ln -s $out ./nix.conf
+ set -e
+ set +o pipefail
+ NIX_CONF_DIR=$PWD \
+ ${cfg.package}/bin/nix show-config ${
+ optionalString (isNixAtLeast "2.3pre")
+ "--no-net --option experimental-features nix-command"
+ } \
+ |& sed -e 's/^warning:/error:/' \
+ | (! grep '${
+ if cfg.checkConfig then "^error:" else "^error: unknown setting"
+ }')
+ set -o pipefail
+ '';
+ };
+
+ semanticConfType = with types;
+ let
+ confAtom = nullOr (oneOf [ bool int float str path package ]) // {
+ description =
+ "Nix config atom (null, bool, int, float, str, path or package)";
+ };
+ in attrsOf (either confAtom (listOf confAtom));
+
+ jsonFormat = pkgs.formats.json { };
+
in {
options.nix = {
+ enable = mkEnableOption ''
+ the Nix configuration module
+ '' // {
+ default = true;
+ visible = false;
+ };
+
+ package = mkOption {
+ type = types.nullOr types.package;
+ default = null;
+ example = literalExpression "pkgs.nix";
+ description = ''
+ The Nix package that the configuration should be generated for.
+ '';
+ };
+
registry = mkOption {
type = types.attrsOf (types.submodule (let
inputAttrs = types.attrsOf
@@ -68,19 +158,81 @@ in {
User level flake registry.
'';
};
+
registryVersion = mkOption {
type = types.int;
default = 2;
internal = true;
description = "The flake registry format version.";
};
- };
- config = mkIf (cfg.registry != { }) {
- xdg.configFile."nix/registry.json".text = builtins.toJSON {
- version = cfg.registryVersion;
- flakes =
- mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
+ checkConfig = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ If enabled (the default), checks for data type mismatches and that Nix
+ can parse the generated nix.conf.
+ '';
+ };
+
+ extraOptions = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ keep-outputs = true
+ keep-derivations = true
+ '';
+ description =
+ "Additional text appended to nix.conf.";
+ };
+
+ settings = mkOption {
+ type = types.submodule { freeformType = semanticConfType; };
+ default = { };
+ example = literalExpression ''
+ {
+ use-sandbox = true;
+ show-trace = true;
+ system-features = [ "big-parallel" "kvm" "recursive-nix" ];
+ }
+ '';
+ description = ''
+ Configuration for Nix, see
+ or
+
+ nix.conf
+ 5
+ for avalaible options.
+ The value declared here will be translated directly to the key-value pairs Nix expects.
+
+
+ Configuration specified in which will be appended
+ verbatim to the resulting config file.
+ '';
};
};
+
+ config = mkIf cfg.enable {
+ assertions = [{
+ assertion = cfg.settings == { } || cfg.package != null;
+ message = ''
+ A corresponding Nix package must be specified via `nix.package` for generating
+ nix.conf.
+ '';
+ }];
+
+ xdg.configFile = {
+ "nix/registry.json" = mkIf (cfg.registry != { }) {
+ source = jsonFormat.generate "registry.json" {
+ version = cfg.registryVersion;
+ flakes =
+ mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
+ };
+ };
+
+ "nix/nix.conf" = mkIf (cfg.settings != { }) { source = nixConf; };
+ };
+ };
+
+ meta.maintainers = [ maintainers.polykernel ];
}
diff --git a/tests/default.nix b/tests/default.nix
index a6acb8e2d..3f93dc9c1 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -45,6 +45,7 @@ import nmt {
./modules/files
./modules/home-environment
./modules/misc/fontconfig
+ ./modules/misc/nix
./modules/programs/alacritty
./modules/programs/alot
./modules/programs/aria2
diff --git a/tests/modules/misc/nix/default.nix b/tests/modules/misc/nix/default.nix
new file mode 100644
index 000000000..b0370c2e4
--- /dev/null
+++ b/tests/modules/misc/nix/default.nix
@@ -0,0 +1,5 @@
+{
+ nix-empty-settings = ./empty-settings.nix;
+ nix-example-settings = ./example-settings.nix;
+ nix-example-registry = ./example-registry.nix;
+}
diff --git a/tests/modules/misc/nix/empty-settings.nix b/tests/modules/misc/nix/empty-settings.nix
new file mode 100644
index 000000000..18f1ab430
--- /dev/null
+++ b/tests/modules/misc/nix/empty-settings.nix
@@ -0,0 +1,13 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ nix = { package = config.lib.test.mkStubPackage { }; };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/nix
+ '';
+ };
+}
diff --git a/tests/modules/misc/nix/example-registry-expected.json b/tests/modules/misc/nix/example-registry-expected.json
new file mode 100644
index 000000000..cf5cee229
--- /dev/null
+++ b/tests/modules/misc/nix/example-registry-expected.json
@@ -0,0 +1,17 @@
+{
+ "flakes": [
+ {
+ "exact": true,
+ "from": {
+ "id": "nixpkgs",
+ "type": "indirect"
+ },
+ "to": {
+ "owner": "my-org",
+ "repo": "my-nixpkgs",
+ "type": "github"
+ }
+ }
+ ],
+ "version": 2
+}
diff --git a/tests/modules/misc/nix/example-registry.nix b/tests/modules/misc/nix/example-registry.nix
new file mode 100644
index 000000000..1875b34d3
--- /dev/null
+++ b/tests/modules/misc/nix/example-registry.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ nix = {
+ registry = {
+ nixpkgs = {
+ to = {
+ type = "github";
+ owner = "my-org";
+ repo = "my-nixpkgs";
+ };
+ };
+ };
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/nix/registry.json \
+ ${./example-registry-expected.json}
+ '';
+ };
+}
diff --git a/tests/modules/misc/nix/example-settings-expected.conf b/tests/modules/misc/nix/example-settings-expected.conf
new file mode 100644
index 000000000..2c2587fd6
--- /dev/null
+++ b/tests/modules/misc/nix/example-settings-expected.conf
@@ -0,0 +1,7 @@
+# WARNING: this file is generated from the nix.settings option in
+# your Home Manager configuration at $XDG_CONFIG_HOME/nix/nix.conf.
+# Do not edit it!
+show-trace = true
+system-features = big-parallel kvm recursive-nix
+use-sandbox = true
+
diff --git a/tests/modules/misc/nix/example-settings.nix b/tests/modules/misc/nix/example-settings.nix
new file mode 100644
index 000000000..f3f9e647e
--- /dev/null
+++ b/tests/modules/misc/nix/example-settings.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ nix = {
+ package = config.lib.test.mkStubPackage {
+ version = lib.getVersion pkgs.nixStable;
+ buildScript = ''
+ target=$out/bin/nix
+ mkdir -p "$(dirname "$target")"
+
+ echo -n "true" > "$target"
+
+ chmod +x "$target"
+ '';
+ };
+
+ settings = {
+ use-sandbox = true;
+ show-trace = true;
+ system-features = [ "big-parallel" "kvm" "recursive-nix" ];
+ };
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/nix/nix.conf \
+ ${./example-settings-expected.conf}
+ '';
+ };
+}