Context
Recently, I wrote a few flakes myself (on a non-NixOS system) using
flake-parts
that define some devshells providing Python environments for my other projects.nixpkgs
does not provide a few specific Python versions one of my legacy projects needs and I started to use nixpkgs-python for these older versions of python. The setup works as I intended.
However, when I tried to add a few more different versions of Python to the setup, I soon realized that because nix is doing input-addressed storage, a lot of the Python packages need to be rebuilt and re-stored since they depend on different versions of the interpreter, but the content of the packages are the same. Even though there is not a lot of rebuilding to happen, I want to take the chance and take a look into the content-addressing experimental feature of nix.However,
nixpkgs-python
does not provide the python modules itself, it copies over the Python packages from its inputnixpkgs
, and to enable content addressing I need to set__contentAddressed = true
on the Python packages of the inputnixpkgs
tonixpkgs-python
.I do find a few other ways that could work, but I feel they are not very ideal:
- I could set
config.contentAddressedByDefault = true
in$HOME/.config/nixpkgs/config.nix
, but this enables CA globally and I would rather not do that to the other parts of my setup.- I could do
python.override {packageOverrides = ...}
at my call site, but this creates a lot of boilerplate and make the intention of the code very hard to read since you need to enumerate through the packages.
I now need a way to modify the default behavior of a flake (here it is nixpkgs
), using either overlays or config, and then supply the customized flake as input to another flake, without modifying the other flake’s code.
I figured that one way to do this is to write a new flake (I call it proxy flake) that emulates itself as nixpkgs flake and modifies the default behavior. However since many call sites of nixpkgs would do import nixpkgs {...}
, that behavior needs to be modified as well and this makes everything a bit complicated. Eventually, I had to pin down a locked flake version and use getFlake
to access unmodified nixpkgs from default.nix
, and this is what I got working:
flake.nix
{
description = "Patched nixpkgs";
inputs.nixpkgs.url = "flake:nixpkgs";
outputs = {self, nixpkgs} :
let
lib = nixpkgs.lib;
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
libVersionInfoOverlay = import
( nixpkgs + "/lib/flake-version-info.nix") nixpkgs;
patchedNixpkgs = nixpkgs // {
legacyPackages = forAllSystems (system:
let
pkgs = (import ./. { inherit system; }).extend (final: prev: {
lib = prev.lib.extend libVersionInfoOverlay;
});
in pkgs
);
};
lockFile = builtins.fromJSON (builtins.readFile ./flake.lock);
lockedNarHash = lockFile.nodes.nixpkgs.locked.narHash;
inputIsLocked = (lockedNarHash == nixpkgs.narHash);
in if inputIsLocked then patchedNixpkgs else abort ''
Input to this patched nixpkgs flake should not be overridden. Doing so may
introduce inconsistent behavior between flake usage and import usage.
Please modify the Patched nixpkgs flake directly if you want to use another
version of nixpkgs as base.
'';
}
default.nix
args:
let
lockFile = builtins.fromJSON (builtins.readFile ./flake.lock);
lockedURL = "github:NixOS/nixpkgs/${lockFile.nodes.nixpkgs.locked.rev}";
origNixpkgs = builtins.getFlake lockedURL;
in
import origNixpkgs (args // {
config.contentAddressedByDefault = true;
})
However this still feels a bit hacky to me, so my questions are:
- What is the recommended way of applying a customized nixpkgs to a part of the system? For whole systems, we have global config/overlays; for individual packages we can use overrides, but it seems to be tricky to go anywhere in between.
- Is there any recommended way of overriding the behavior of an input flake to another flake without modifying its code? Is making a proxy flake a good idea/practice?
- How to address the problem of
default.nix
not having the context of flake inputs when building a proxy flake for nixpkgs? Is pinning down the flake version the only way to do it?