most dev flake inputs flake=false#4294
Conversation
mightyiam
left a comment
There was a problem hiding this comment.
For what it's worth, I've seen worse lockfiles. I've had worse lockfiles. The follows situation here seems minimal. And I don't think that the approach I'm suggesting here is terribly popular, but I think it's based.
| "gitignore": { | ||
| "inputs": { | ||
| "nixpkgs": [ | ||
| "git-hooks", | ||
| "nixpkgs" | ||
| ] | ||
| }, | ||
| "locked": { | ||
| "lastModified": 1709087332, | ||
| "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", | ||
| "owner": "hercules-ci", | ||
| "repo": "gitignore.nix", | ||
| "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", | ||
| "type": "github" | ||
| }, | ||
| "original": { | ||
| "owner": "hercules-ci", | ||
| "repo": "gitignore.nix", | ||
| "type": "github" | ||
| } | ||
| }, |
There was a problem hiding this comment.
Here is one input less 🕺
| { | ||
| imports = [ | ||
| inputs.devshell.flakeModule | ||
| (inputs.devshell + "/flake-module.nix") |
| # NOTE: Use a different name to the root flake's inputs.nixpkgs to avoid shadowing it. | ||
| # NOTE: The only reason we specify a nixpkgs input at all here, is so the other inputs can follow it. | ||
| # TODO: Once nix 2.26 is more prevalent, follow the root flake's inputs using a "path:../.." input. | ||
| dev-nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; |
There was a problem hiding this comment.
Nixpkgs will likely never have its own flake inputs, so I didn't bother.
There was a problem hiding this comment.
flake-compat is also unlikely to have any of its own flake inputs, so I didn't bother.
There was a problem hiding this comment.
flake-compat documents flake = false as its recommended usage.
| nuschtosSearch = { | ||
| # TODO: newer versions fail with: TimeoutError: The operation was aborted due to timeout | ||
| url = "github:NuschtOS/search/b6f77b88e9009bfde28e2130e218e5123dc66796"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| }; |
There was a problem hiding this comment.
This input does not seem to support being used as vanilla. It seems possible with a few lines, but I don't think that we should take that on, especially since it seems to pin its ixx input to some particular version that corresponds with something internal.
| # keep-sorted start block=yes newline_separated=yes | ||
| devshell = { | ||
| url = "github:numtide/devshell"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; |
There was a problem hiding this comment.
I'm not entirely sold on flake = false being worth it just for eliminating transitive follows.
Imagine the scenario where an input changes its inputs without us noticing; if we pull it as a flake, then we get their new inputs transitively. If we don't, then things potentially break.
If the reason we can set flake = false is that we are explicitly only using a part of the project that is designed to be used without flake inputs, then that's fine and should probably be documented in a comment.
# Don't pull flake inputs or evaluate outputs.
# We exclusively use the flake-module file.
flake = false;Or perhaps a top-level comment along the lines of:
# Avoid `flake = true` when we do not need to evaluate flake outputs or fetch flake inputs.
# e.g. when we only import loose files.TL;DR a) consider whether we're setting flake = false for the right reasons b) consider whether flake = false is fragile and may miss future transitive dependencies c) document why we enable or disable flake in general or for specific inputs.
There was a problem hiding this comment.
I've added that single comment above the inputs attribute.
I've pointed out in this PR for each flake=false why it is okay.
I think that projects that expose truly loose files such as /flake-module.nix and /lib/default.nix are aware that users will use them and are not likely to turn them into functions that first take local context such as flake inputs. I prefer this risk over lockfile drift.
| { config, ... }: | ||
| { | ||
| nixpkgs = { | ||
| source = self.inputs.dev-nixpkgs; |
There was a problem hiding this comment.
In the case of this input, the follows actually has a purpose. For what it's worth, I think that the passing of an explicit nixpkgs this way makes more sense, due to it being located here instead of in the flake.nix.
There was a problem hiding this comment.
We should prefer inputs.nixpkgs over inputs.dev-nixpkgs.
We enforce that they are the same FOD, but dev-nixpkgs is an implementation detail of the dev-lockfile.
There was a problem hiding this comment.
I'll assume you are correct about what we should be doing, but in main, the nix-darwin input has inputs.nixpkgs.follows = "dev-nixpkgs";. Would you like a separate PR that changes that?
There was a problem hiding this comment.
in
main, the nix-darwin input hasinputs.nixpkgs.follows = "dev-nixpkgs";.
In main it is necessary, because of the way nix flake lock works; we cannot follow an input from the top-level lockfile so we introduced dev-nixpkgs as a workaround.
inputs.dev-nixpkgs is a mirror of the top-level flake's inputs.nixpkgs and should only be used when the workaround is necessary.
Or to quote the inline comment:
The only reason we specify a nixpkgs input at all here, is so the other inputs can follow it.
As of this PR, I believe the only input that needs dev-nixpkgs is nuschtosSearch.
| lib, | ||
| }: | ||
| self.inputs.nix-darwin.lib.darwinSystem { | ||
| (import (self.inputs.nix-darwin + "/eval-config.nix")) { |
There was a problem hiding this comment.
Is it worth naming this function?
| (import (self.inputs.nix-darwin + "/eval-config.nix")) { | |
| let | |
| darwinSystem = import (self.inputs.nix-darwin + "/eval-config.nix"); | |
| in | |
| darwinSystem { |
Is this a publicly documented usage pattern? I.e. the "official" way to consume nix-darwin without flakes?
There was a problem hiding this comment.
Also, because flake inputs are already in-store objects, we could consider using:
"${self.inputs.nix-darwin}/eval-config.nix"string interpolation.
IIUC, this shouldn't copy-to-store if the path is already a store-object.
Either approach is fine by me, though. You current approach is technically more robust at avoiding potential copy-to-store operations.
| let | ||
| inherit (self.inputs.home-manager.lib) | ||
|
|
||
| inherit (import (self.inputs.home-manager + "/lib") { inherit lib; }) |
There was a problem hiding this comment.
For readability, is it worth doing something like:
| inherit (import (self.inputs.home-manager + "/lib") { inherit lib; }) | |
| hmLib = import (self.inputs.home-manager + "/lib") { inherit lib; }; | |
| inherit (hmLib) |
?
| # To be clear, this is an upstream module system bug, this test validates our workaround. | ||
| let | ||
| inherit (self.inputs.home-manager.lib) | ||
| inherit (import (self.inputs.home-manager + "/lib") { inherit lib; }) |
| lib, | ||
| }: | ||
| self.inputs.home-manager.lib.homeManagerConfiguration { | ||
| (import (self.inputs.home-manager + "/lib") { inherit lib; }).homeManagerConfiguration { |
|
It's worth noting that the dev-lockfile you've worked on here is not user-facing. It does not get evaluated by end-users unless they evaluate things like our test suite or dev shell. |
I believe this is documented in CONTRIBUTING.md. IIRC, we still have our entire test suite in the We have a |
6c4c27f to
d0b625b
Compare
mightyiam
left a comment
There was a problem hiding this comment.
I totally get that this whole change does not affect users.
Well, here are some changes. Please let me know whether I missed anything.
| devshell = { | ||
| url = "github:numtide/devshell"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| flake = false; |
There was a problem hiding this comment.
Exposes a vanilla /flake-module.nix, so we're good.
| url = "github:cachix/git-hooks.nix"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| inputs.flake-compat.follows = "flake-compat"; | ||
| flake = false; |
There was a problem hiding this comment.
Exposes a vanilla /flake-module.nix, so we're good.
| home-manager = { | ||
| url = "github:nix-community/home-manager"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| flake = false; |
There was a problem hiding this comment.
Provides a /lib/default.nix that takes { inherit lib; } so we're golden.
| treefmt-nix = { | ||
| url = "github:numtide/treefmt-nix"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| flake = false; |
There was a problem hiding this comment.
Provides a vanilla /flake-module.nix so we're golden.
| flake-compat.url = "github:NixOS/flake-compat"; | ||
| flake-compat = { | ||
| url = "github:NixOS/flake-compat"; | ||
| flake = false; |
There was a problem hiding this comment.
As @MattSturgeon pointed, flake-compat instructs declaring it with flake=false in its readme.
| # keep-sorted start block=yes newline_separated=yes | ||
| devshell = { | ||
| url = "github:numtide/devshell"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; |
There was a problem hiding this comment.
I've added that single comment above the inputs attribute.
I've pointed out in this PR for each flake=false why it is okay.
I think that projects that expose truly loose files such as /flake-module.nix and /lib/default.nix are aware that users will use them and are not likely to turn them into functions that first take local context such as flake inputs. I prefer this risk over lockfile drift.
| nix-darwin = { | ||
| url = "github:lnl7/nix-darwin"; | ||
| inputs.nixpkgs.follows = "dev-nixpkgs"; | ||
| flake = false; |
There was a problem hiding this comment.
Provides a vanilla /eval-config.nix so we're golden.
| { config, ... }: | ||
| { | ||
| nixpkgs = { | ||
| source = self.inputs.dev-nixpkgs; |
There was a problem hiding this comment.
I'll assume you are correct about what we should be doing, but in main, the nix-darwin input has inputs.nixpkgs.follows = "dev-nixpkgs";. Would you like a separate PR that changes that?
d0b625b to
4d35f4d
Compare
This sets
flake=falsefor 5 out of the 8 dev flake inputs.These 5 inputs seem to support being used as vanilla nix.
This eliminates the
inputs.<name>.inputs.<name>.follows.<name>shenanigans.It also eliminates the risk of failing to notice that an input has a new input.
Yes, we benefit from some flake features, such as purity, eval cache and input pinning.
But we don't have to use inputs as flakes if that incurs a cost and no benefit.
This is how I personally would like to consume flake inputs whenever they support it, including NixVim.
That is discussed in #4288
and is mostly decoupled from this change.
By the way, I tried
nix flake checkin this repo and ran out of memory.What's the trick to succeed?