Description
(was: Solve dev(/test) dependencies and bloated, diverging lock files, by reconsidering the flattened lock file)
Is your feature request related to a problem? Please describe.
We've previously identified issues with the way the lock file currently works.
- Irrelevant development dependencies are added to the lock files of all dependent flakes
- Mututally dependent flakes cause diverging lock files as each lock file update incorporates a new version with a chain of irrelevant dependencies.
- issue?
- Some users just complain about the size of their lock file. Partly pedantic, but they're actually kind of right:
- Ideally, lock file changes are reviewable, so that nothing weird creeps in through contributions that have such updates.
Describe the solution you'd like
We may consider two scenarios; one with follows
and one without. Let's start without follows
.
Observing that we do not exploit the apparent benefit of knowing all inputs upfront, we may consider loading the required parts of dependency lockfiles on demand.
Example scenario
- Dependencies
.
->foo
->nixpkgs
- No
follows
.
Evaluation order, current situation:
- need
packages.default
- need
inputs.foo.packages.foo
- read
foo
details from./flake.lock
- fetch
foo
- need
foo.inputs.nixpkgs
- read
nixpkgs_1
details from local lock file - fetch
nixpkgs_1
Same evaluation, partial lock file
- need
packages.default
- need
inputs.foo.packages.foo
- read
foo
details from local lock file - fetch
foo
- need
foo.inputs.nixpkgs
- read
nixpkgs
details fromfoo/flake.lock
- fetch
nixpkgs
As you can see, the fetching characteristics remain the same.
Let's reintroduce follows
, and first consider the example:
inputs.foo.nixpkgs.follows = "nixpkgs";
In other words, this injects the local nixpkgs
dependency into foo
. The lazy fetching behavior remains the same. This injection should be handled by call-flake.nix
and does not require changes to the lock file.
Now consider the opposite follows
:
inputs.nixpkgs.follows = "foo.nixpkgs";
In this case, if we were not to include a copy of foo.nixpkgs
details in the local lock file, we'd have a slightly worse lazy fetching behavior, as nixpkgs
would only be fetchable after foo
's lock has been fetched. For this case, we do want "flat lock file" behavior, but limited to this dependency. The rule for which transitive dependencies to include in the lock file might be as simple as direct inputs + inputs referenced in follows
.
This suggests the following implementation strategy:
- When generating a lock, apply the above rule to determine which flakes to traverse and include in the lock file
- When calling a flake, call it such that its
inputs
are those from its own lock//
those set in the current lock- Lazy trees #6530 already adds an
overrides
parameter incall-flake.nix
that appears to enable this. The missing bit is that some/many nodes would be created by reinvokingcall-flake.nix
rather than sharing the node identifiers. Node identifiers are local to eachcall-flake.nix
call. i.e.overrides
is a prerequisite for calling dependency locks.
- Lazy trees #6530 already adds an
Describe alternatives you've considered
Mark development dependencies explicitly.
- This is more configuration which is generally not good.
- This adds more corner cases and error messages.
- This does not solve the problem of having mutually recursive development dependencies. Note that we can barely manage to make builds not mutually recursive, but flakes encompass more than builds, so expecting flakes not to be mutually recursive is not realistic.
Additional context
Priorities
Add 👍 to issues you find important.
Metadata
Assignees
Labels
Type
Projects
Status
🏁 Review
Status
Defined work