From 8d5e27b4807d25308dfe369d5a923d87e7dbfda3 Mon Sep 17 00:00:00 2001 From: midchildan Date: Thu, 13 Jun 2024 10:47:38 +0900 Subject: [PATCH] nix: add a declarative alternative to Nix channels (#4031) * nix: add options 'nixPath' and 'keepOldNixPath' By default, the system value for $NIX_PATH is kept as a fallback. To completely override the system value for $NIX_PATH: nix.keepOldNixPath = false; * nix: add more tests * nix: add a declarative alternative to Nix channels This adds a new option, 'nix.channels'. It's the Nix channels equivalent of the 'nix.registry' option, and compatible with pre-Flake Nix tooling including nix-env and nix-shell. Like 'nix.registry', this option is useful for pinning Nix channels. Channels defined in the new option can coexist with channels introduced through the nix-channel command. If the same channel exists in both, the one from Home Manager will be prioritized. * nix: add news entry * nix: make channels respect use-xdg-base-directories * nix: remove 'with lib;' --------- Co-authored-by: Michael Hoang --- modules/misc/news.nix | 11 +++ modules/misc/nix.nix | 83 ++++++++++++++++++- tests/modules/misc/nix/default.nix | 3 + tests/modules/misc/nix/empty-settings.nix | 1 + .../modules/misc/nix/example-channels-xdg.nix | 29 +++++++ tests/modules/misc/nix/example-channels.nix | 26 ++++++ tests/modules/misc/nix/example-settings.nix | 5 ++ tests/modules/misc/nix/keep-old-nix-path.nix | 16 ++++ 8 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 tests/modules/misc/nix/example-channels-xdg.nix create mode 100644 tests/modules/misc/nix/example-channels.nix create mode 100644 tests/modules/misc/nix/keep-old-nix-path.nix diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 2164d66a..1242d640 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -1656,6 +1656,17 @@ in { See https://codeberg.org/dnkl/yambar for more. ''; } + + { + time = "2024-05-25T14:36:03+00:00"; + message = '' + Multiple new options are available: + + - 'nix.nixPath' + - 'nix.keepOldNixPath' + - 'nix.channels' + ''; + } ]; }; } diff --git a/modules/misc/nix.nix b/modules/misc/nix.nix index a4456946..2daa56b2 100644 --- a/modules/misc/nix.nix +++ b/modules/misc/nix.nix @@ -1,15 +1,40 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit (lib) + boolToString concatStringsSep escape floatToString getVersion isBool + isConvertibleWithToString isDerivation isFloat isInt isList isString + literalExpression maintainers mapAttrsToList mkDefault mkEnableOption mkIf + mkMerge mkOption optionalString toPretty types versionAtLeast; + cfg = config.nix; nixPackage = cfg.package; isNixAtLeast = versionAtLeast (getVersion nixPackage); + nixPath = concatStringsSep ":" cfg.nixPath; + + useXdg = config.nix.enable + && (config.nix.settings.use-xdg-base-directories or false); + defexprDir = if useXdg then + "${config.xdg.stateHome}/nix/defexpr" + else + "${config.home.homeDirectory}/.nix-defexpr"; + + # The deploy path for declarative channels. The directory name is prefixed + # with a number to make it easier for files in defexprDir to control the order + # they'll be read relative to each other. + channelPath = "${defexprDir}/50-home-manager"; + + channelsDrv = let + mkEntry = name: drv: { + inherit name; + path = toString drv; + }; + in pkgs.linkFarm "channels" (lib.mapAttrsToList mkEntry cfg.channels); + nixConf = assert isNixAtLeast "2.2"; let @@ -102,6 +127,47 @@ in { ''; }; + nixPath = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ + "$HOME/.nix-defexpr/channels" + "darwin-config=$HOME/.config/nixpkgs/darwin-configuration.nix" + ]; + description = '' + Adds new directories to the Nix expression search path. + + Used by Nix when looking up paths in angular brackets + (e.g. ``). + ''; + }; + + keepOldNixPath = mkOption { + type = types.bool; + default = true; + example = false; + description = '' + Whether {option}`nix.nixPath` should keep the previously set values in + {env}`NIX_PATH`. + ''; + }; + + channels = lib.mkOption { + type = with lib.types; attrsOf package; + default = { }; + example = lib.literalExpression "{ inherit nixpkgs; }"; + description = '' + A declarative alternative to Nix channels. Whereas with stock channels, + you would register URLs and fetch them into the Nix store with + {manpage}`nix-channel(1)`, this option allows you to register the store + path directly. One particularly useful example is registering flake + inputs as channels. + + This option can coexist with stock Nix channels. If the same channel is + defined in both, this option takes precedence. + ''; + }; + registry = mkOption { type = types.attrsOf (types.submodule (let inputAttrs = types.attrsOf @@ -210,6 +276,19 @@ in { }; config = mkIf cfg.enable (mkMerge [ + (mkIf (cfg.nixPath != [ ] && !cfg.keepOldNixPath) { + home.sessionVariables.NIX_PATH = "${nixPath}"; + }) + + (mkIf (cfg.nixPath != [ ] && cfg.keepOldNixPath) { + home.sessionVariables.NIX_PATH = "${nixPath}\${NIX_PATH:+:$NIX_PATH}"; + }) + + (lib.mkIf (cfg.channels != { }) { + nix.nixPath = [ channelPath ]; + home.file."${channelPath}".source = channelsDrv; + }) + (mkIf (cfg.registry != { }) { xdg.configFile."nix/registry.json".source = jsonFormat.generate "registry.json" { diff --git a/tests/modules/misc/nix/default.nix b/tests/modules/misc/nix/default.nix index b0370c2e..4c8822bf 100644 --- a/tests/modules/misc/nix/default.nix +++ b/tests/modules/misc/nix/default.nix @@ -2,4 +2,7 @@ nix-empty-settings = ./empty-settings.nix; nix-example-settings = ./example-settings.nix; nix-example-registry = ./example-registry.nix; + nix-keep-old-nix-path = ./keep-old-nix-path.nix; + nix-example-channels = ./example-channels.nix; + nix-example-channels-xdg = ./example-channels-xdg.nix; } diff --git a/tests/modules/misc/nix/empty-settings.nix b/tests/modules/misc/nix/empty-settings.nix index 18f1ab43..7e584474 100644 --- a/tests/modules/misc/nix/empty-settings.nix +++ b/tests/modules/misc/nix/empty-settings.nix @@ -8,6 +8,7 @@ with lib; nmt.script = '' assertPathNotExists home-files/.config/nix + assertPathNotExists home-files/.nix-defexpr/50-home-manager ''; }; } diff --git a/tests/modules/misc/nix/example-channels-xdg.nix b/tests/modules/misc/nix/example-channels-xdg.nix new file mode 100644 index 00000000..8ca1569f --- /dev/null +++ b/tests/modules/misc/nix/example-channels-xdg.nix @@ -0,0 +1,29 @@ +{ lib, config, pkgs, ... }: + +let + exampleChannel = pkgs.writeTextDir "default.nix" '' + { pkgs ? import { } }: + + { + example = pkgs.emptyDirectory; + } + ''; +in { + config = { + nix = { + package = config.lib.test.mkStubPackage { + version = lib.getVersion pkgs.nixVersions.stable; + }; + channels.example = exampleChannel; + settings.use-xdg-base-directories = true; + }; + + nmt.script = '' + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export NIX_PATH="/home/hm-user/.local/state/nix/defexpr/50-home-manager''${NIX_PATH:+:$NIX_PATH}"' + assertFileContent \ + home-files/.local/state/nix/defexpr/50-home-manager/example/default.nix \ + ${exampleChannel}/default.nix + ''; + }; +} diff --git a/tests/modules/misc/nix/example-channels.nix b/tests/modules/misc/nix/example-channels.nix new file mode 100644 index 00000000..4b98c55d --- /dev/null +++ b/tests/modules/misc/nix/example-channels.nix @@ -0,0 +1,26 @@ +{ config, pkgs, ... }: + +let + exampleChannel = pkgs.writeTextDir "default.nix" '' + { pkgs ? import { } }: + + { + example = pkgs.emptyDirectory; + } + ''; +in { + config = { + nix = { + package = config.lib.test.mkStubPackage { }; + channels.example = exampleChannel; + }; + + nmt.script = '' + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export NIX_PATH="/home/hm-user/.nix-defexpr/50-home-manager''${NIX_PATH:+:$NIX_PATH}"' + assertFileContent \ + home-files/.nix-defexpr/50-home-manager/example/default.nix \ + ${exampleChannel}/default.nix + ''; + }; +} diff --git a/tests/modules/misc/nix/example-settings.nix b/tests/modules/misc/nix/example-settings.nix index 6cbcd9df..df62fe8d 100644 --- a/tests/modules/misc/nix/example-settings.nix +++ b/tests/modules/misc/nix/example-settings.nix @@ -17,6 +17,8 @@ with lib; ''; }; + nixPath = [ "/a" "/b/c" ]; + settings = { use-sandbox = true; show-trace = true; @@ -28,6 +30,9 @@ with lib; assertFileContent \ home-files/.config/nix/nix.conf \ ${./example-settings-expected.conf} + + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export NIX_PATH="/a:/b/c''${NIX_PATH:+:$NIX_PATH}"' ''; }; } diff --git a/tests/modules/misc/nix/keep-old-nix-path.nix b/tests/modules/misc/nix/keep-old-nix-path.nix new file mode 100644 index 00000000..72bd9f16 --- /dev/null +++ b/tests/modules/misc/nix/keep-old-nix-path.nix @@ -0,0 +1,16 @@ +{ config, ... }: + +{ + config = { + nix = { + package = config.lib.test.mkStubPackage { }; + nixPath = [ "/a" "/b/c" ]; + keepOldNixPath = false; + }; + + nmt.script = '' + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export NIX_PATH="/a:/b/c"' + ''; + }; +}