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 <enzime@users.noreply.github.com>
This commit is contained in:
midchildan 2024-06-13 10:47:38 +09:00 committed by GitHub
parent 892f76bd0a
commit 8d5e27b480
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 172 additions and 2 deletions

View file

@ -1656,6 +1656,17 @@ in {
See https://codeberg.org/dnkl/yambar for more. 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'
'';
}
]; ];
}; };
} }

View file

@ -1,15 +1,40 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib;
let 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; cfg = config.nix;
nixPackage = cfg.package; nixPackage = cfg.package;
isNixAtLeast = versionAtLeast (getVersion nixPackage); 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"; nixConf = assert isNixAtLeast "2.2";
let 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. `<nixpkgs>`).
'';
};
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 { registry = mkOption {
type = types.attrsOf (types.submodule (let type = types.attrsOf (types.submodule (let
inputAttrs = types.attrsOf inputAttrs = types.attrsOf
@ -210,6 +276,19 @@ in {
}; };
config = mkIf cfg.enable (mkMerge [ 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 != { }) { (mkIf (cfg.registry != { }) {
xdg.configFile."nix/registry.json".source = xdg.configFile."nix/registry.json".source =
jsonFormat.generate "registry.json" { jsonFormat.generate "registry.json" {

View file

@ -2,4 +2,7 @@
nix-empty-settings = ./empty-settings.nix; nix-empty-settings = ./empty-settings.nix;
nix-example-settings = ./example-settings.nix; nix-example-settings = ./example-settings.nix;
nix-example-registry = ./example-registry.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;
} }

View file

@ -8,6 +8,7 @@ with lib;
nmt.script = '' nmt.script = ''
assertPathNotExists home-files/.config/nix assertPathNotExists home-files/.config/nix
assertPathNotExists home-files/.nix-defexpr/50-home-manager
''; '';
}; };
} }

View file

@ -0,0 +1,29 @@
{ lib, config, pkgs, ... }:
let
exampleChannel = pkgs.writeTextDir "default.nix" ''
{ pkgs ? import <nixpkgs> { } }:
{
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
'';
};
}

View file

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
let
exampleChannel = pkgs.writeTextDir "default.nix" ''
{ pkgs ? import <nixpkgs> { } }:
{
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
'';
};
}

View file

@ -17,6 +17,8 @@ with lib;
''; '';
}; };
nixPath = [ "/a" "/b/c" ];
settings = { settings = {
use-sandbox = true; use-sandbox = true;
show-trace = true; show-trace = true;
@ -28,6 +30,9 @@ with lib;
assertFileContent \ assertFileContent \
home-files/.config/nix/nix.conf \ home-files/.config/nix/nix.conf \
${./example-settings-expected.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}"'
''; '';
}; };
} }

View file

@ -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"'
'';
};
}