Skip to content

Commit

Permalink
nixos/flake: options to set up registry and nixPath for flake configs
Browse files Browse the repository at this point in the history
Currently there are a bunch of really wacky hacks required to get nixpkgs
path correctly set up under flake configs such that `nix run
nixpkgs#hello` and `nix run -f '<nixpkgs>' hello` hit the nixpkgs that
the system was built with. In particular you have to use specialArgs or
an anonymous module, and everyone has to include this hack in their
own configs.

We can do this for users automatically.

I have tested these manually with a basic config; I don't know if it is
even possible to write a nixos test for it since you can't really get a
string-with-context to yourself unless you are in a flake context.
  • Loading branch information
lf- committed Oct 1, 2023
1 parent 366e586 commit 3120b3e
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 53 deletions.
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
system.nixos.versionSuffix =
".${final.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}.${self.shortRev or "dirty"}";
system.nixos.revision = final.mkIf (self ? rev) self.rev;
nixpkgs.flake.source = final.mkDefault (toString self);
}];
} // lib.optionalAttrs (! args?system) {
# Allow system to be set modularly in nixpkgs.system.
Expand Down
6 changes: 6 additions & 0 deletions nixos/doc/manual/release-notes/rl-2311.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

- LXD now supports virtual machine instances to complement the existing container support

- NIX_PATH and the system flake registry will be automatically set up for
flake-based NixOS configurations if
[nixpkgs.flake.setNixPath](#opt-nixpkgs.flake.setNixPath) and
[nixpkgs.flake.setFlakeRegistry](#opt-nixpkgs.flake.setFlakeRegistry) are
set, respectively. These will be made true by default in a future release.

## New Services {#sec-release-23.11-new-services}

- [MCHPRS](https://github.com/MCHPR/MCHPRS), a multithreaded Minecraft server built for redstone. Available as [services.mchprs](#opt-services.mchprs.enable).
Expand Down
10 changes: 10 additions & 0 deletions nixos/modules/config/nix.nix
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ in
always allowed to connect.
'';
};

flake-registry = mkOption {
type = types.str;
default = "https://channels.nixos.org/flake-registry.json";
description = lib.mdDoc ''
Path or URI of the global flake registry.
When empty, disables the global flake registry.
'';
};
};
};
default = { };
Expand Down
164 changes: 111 additions & 53 deletions nixos/modules/misc/nixpkgs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -328,64 +328,122 @@ in
Ignored when `nixpkgs.pkgs`, `nixpkgs.localSystem` or `nixpkgs.hostPlatform` is set.
'';
};
};

config = {
_module.args = {
pkgs =
# We explicitly set the default override priority, so that we do not need
# to evaluate finalPkgs in case an override is placed on `_module.args.pkgs`.
# After all, to determine a definition priority, we need to evaluate `._type`,
# which is somewhat costly for Nixpkgs. With an explicit priority, we only
# evaluate the wrapper to find out that the priority is lower, and then we
# don't need to evaluate `finalPkgs`.
lib.mkOverride lib.modules.defaultOverridePriority
finalPkgs.__splicedPackages;
};
flake = {
source = mkOption {
type = types.nullOr types.str;

assertions = let
# Whether `pkgs` was constructed by this module. This is false when any of
# nixpkgs.pkgs or _module.args.pkgs is set.
constructedByMe =
# We set it with default priority and it can not be merged, so if the
# pkgs module argument has that priority, it's from us.
(lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
== lib.modules.defaultOverridePriority
# Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
&& !opt.pkgs.isDefined;
in [
(
let
nixosExpectedSystem =
if config.nixpkgs.crossSystem != null
then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
nixosOption =
if config.nixpkgs.crossSystem != null
then "nixpkgs.crossSystem"
else "nixpkgs.localSystem";
pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
in {
assertion = constructedByMe -> !hasPlatform -> nixosExpectedSystem == pkgsSystem;
message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
}
)
{
assertion = constructedByMe -> hasPlatform -> legacyOptionsDefined == [];
message = ''
Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
${hostPlatformLine
}${buildPlatformLine
}
However, it also defines the legacy options:
${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
For a future proof system configuration, we recommend to remove
the legacy definitions.
default = null;

description = mdDoc ''
The derivation to the nixpkgs sources, as a string with context. This
is set up by the nixpkgs flake.
'';
};

setNixPath = mkOption {
type = types.bool;

default = false;

description = mdDoc ''
Whether to create /etc/nix/inputs/nixpkgs as a symlink and set
NIX_PATH to include /etc/nix/inputs/nixpkgs.
This will be true by default for flake-based configurations in a
future release.
'';
};

setFlakeRegistry = mkOption {
type = types.bool;

default = false;

description = mdDoc ''
Whether to set nixpkgs in the local flake registry and disable the
external flake registry.
This will be true by default for flake-based configurations in a
future release.
'';
}
];
};
};
};

config = mkMerge [
{
_module.args = {
pkgs =
# We explicitly set the default override priority, so that we do not need
# to evaluate finalPkgs in case an override is placed on `_module.args.pkgs`.
# After all, to determine a definition priority, we need to evaluate `._type`,
# which is somewhat costly for Nixpkgs. With an explicit priority, we only
# evaluate the wrapper to find out that the priority is lower, and then we
# don't need to evaluate `finalPkgs`.
lib.mkOverride lib.modules.defaultOverridePriority
finalPkgs.__splicedPackages;
};

assertions = let
# Whether `pkgs` was constructed by this module. This is false when any of
# nixpkgs.pkgs or _module.args.pkgs is set.
constructedByMe =
# We set it with default priority and it can not be merged, so if the
# pkgs module argument has that priority, it's from us.
(lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
== lib.modules.defaultOverridePriority
# Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
&& !opt.pkgs.isDefined;
in [
(
let
nixosExpectedSystem =
if config.nixpkgs.crossSystem != null
then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
nixosOption =
if config.nixpkgs.crossSystem != null
then "nixpkgs.crossSystem"
else "nixpkgs.localSystem";
pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
in {
assertion = constructedByMe -> !hasPlatform -> nixosExpectedSystem == pkgsSystem;
message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
}
)
{
assertion = constructedByMe -> hasPlatform -> legacyOptionsDefined == [];
message = ''
Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
${hostPlatformLine
}${buildPlatformLine
}
However, it also defines the legacy options:
${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
For a future proof system configuration, we recommend to remove
the legacy definitions.
'';
}
];
}
(mkIf (cfg.flake.source != null) (mkMerge [
(mkIf cfg.flake.setNixPath {
environment.etc."nix/inputs/nixpkgs".source = mkDefault cfg.flake.source;
nix.nixPath = mkDefault [ "nixpkgs=/etc/nix/inputs/nixpkgs" ];
})
(mkIf cfg.flake.setFlakeRegistry {
nix.registry.nixpkgs.to = mkDefault {
type = "path";
path = cfg.flake.source;
};
# This is set due to a Nix bug where the registry json is fetched on
# every `nix` invocation even if the requested flake is entirely local.
nix.settings.flake-registry = mkDefault "";
})
]))
];

# needs a full nixpkgs path to import nixpkgs
meta.buildDocsInSandbox = false;
}

0 comments on commit 3120b3e

Please sign in to comment.