flake-parts.partitions
This module provides a performance optimization, and a way to reduce the size of the main flake.lock
that is currently getting copied into all consuming flakes' lock files, which bothers some users.
The goals of this module are:
-
Do not load irrelevant modules when you evaluate an attribute of an already locked flake, it . If the flake was locked remotely, the sources for those dependencies would still have to be fetched. This is achieved by moving some
imports
and their related definitons intopartitions.<name>.module
and definingpartitionAttrs.<attr>
to point to that partition. -
Don't copy irrelevant lock entries when you lock a flake that has the current flake as its input. This can be achieved by moving inputs into a subflake whose only responsibility is to provide the inputs, and then pointing
partitions.<name>.extraInputsFlake
to that subflake.
Example
Definitions in the mkFlake
root module (or a direct import into it):
partitionedAttrs.checks = "dev";
partitionedAttrs.devShells = "dev";
partitionedAttrs.herculesCI = "dev";
partitions.dev.extraInputsFlake = ./dev;
partitions.dev.module = { inputs, ... }: {
imports = [
./nix/development.nix
inputs.hercules-ci-effects.flakeModule
inputs.git-hooks-nix.flakeModule
];
};
dev/flake.nix
:
{
description = "Private inputs for development purposes. These are used by the top level flake in the `dev` partition, but do not appear in consumers' lock files.";
inputs = {
hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects";
git-hooks-nix.url = "github:cachix/git-hooks.nix";
# See https://github.com/ursi/get-flake/issues/4
git-hooks-nix.inputs.nixpkgs.follows = "";
};
# This flake is only used for its inputs.
outputs = { ... }: { };
}
Caveats
-
A module in a partition can affect options that are not exported with
partitionedAttrs
. This will have an effect that is only observable in the attributes that are exported withpartitionedAttrs
, and not in the other flake attributes. This could be useful or surprising. -
A flake used in
extraInputsFlake
is a separate flake, which means that you may have to duplicate e.g. anixpkgs
input for the purpose offollows
. We don't have a way to override flake inputs after the effect.
Explanation
imports
must be loaded eagerly by the module system, because any module can affect the contents and shape of config
.
Declaring imports = [ foo ];
is simply not enough to know the effects of foo
without getting foo
's attributes.
Fortunately this fundamental limitation only applies one "layer" at a time. Every submodule has its own set of modules or imports
and that allows us to still load modules lazily, as long as we provide the means for those modules to affect the root (here: the flake) in a controlled way. That is what this module does. It loads the "optional" modules into a separate submodule, and provides an option to load parts of that (sub)module evaluation into the root.
Installation
To use these options, add inside the mkFlake
:
imports = [
inputs.flake-parts.flakeModules.partitions
];
Run nix flake lock
and you're set.
Options
partitionedAttrs
A set of flake output attributes that are taken from a partition instead of the default top level flake-parts evaluation.
The attribute name refers to the flake output attribute name, and the value is the name of the partition to use.
The flake attributes are overridden with lib.mkForce
priority.
See the partitions
options to understand the purpose.
Type: attribute set of string
Default:
{ }
Example:
{
checks = "dev";
devShells = "dev";
herculesCI = "dev";
}
Declared by:
partitions
By partitioning the flake, you can avoid fetching inputs that are not needed for the evaluation of a particular attribute.
Each partition is a distinct module system evaluation. This allows
attributes of the final flake to be defined by multiple sets of modules,
so that for example the packages
attribute can be evaluated without
loading development related inputs.
While the module system does a good job at preserving laziness, the fact
that a development related import can define packages
means that
in order to evaluate packages
, you need to evaluate at least to the
point where you can conclude that the development related import does
not actually define a packages
attribute. While the actual evaluation
is cheap, it can only happen after fetching the input, which is not
as cheap.
Type: attribute set of (submodule)
Default:
{ }
Example:
{
dev = {
extraInputsFlake = ./dev;
module = ./dev/flake-module.nix;
};
}
Declared by:
partitions.<name>.extraInputs
Extra inputs to add to the inputs module argument in the partition.
This can be used as a workaround for the fact that transitive inputs are locked in the “end user” flake. That’s not desirable for inputs they don’t need, such as development inputs.
Type: lazy attribute set of raw value
Default:
if extraInputsFlake
is set, then builtins.getFlake extraInputsFlake
, else { }
Declared by:
partitions.<name>.extraInputsFlake
Location of a flake whose inputs to add to the inputs module argument in the partition.
Note that flake follows
are resolved without any awareness of inputs that are not in the flake.
As a consequence, a follows
entry in the flake inputs can not refer to inputs that are not in that specific flake.
Implementation note: if the type of extraInputsFlake
is a path, it is loaded with an expression-based reimplementation of builtins.getFlake
, as getFlake
is incapable of loading paths in pure mode as of writing.
Type: raw value
Example:
./dev
Declared by:
partitions.<name>.module
A re-evaluation of the flake-parts top level modules.
You may define config definitions, imports
, etc here, and it can be read like any other submodule.
Type: submodule
Default:
{ }
Example:
{
imports = [
./dev/flake-module.nix
];
}
Declared by: