Skip to content

most dev flake inputs flake=false - #4294

Open
mightyiam wants to merge 6 commits into
nix-community:mainfrom
mightyiam:dev-inputs-flake-false
Open

mightyiam wants to merge 6 commits into
nix-community:mainfrom
mightyiam:dev-inputs-flake-false

Conversation

@mightyiam

Copy link
Copy Markdown
Member

This sets flake=false for 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 check in this repo and ran out of memory.
What's the trick to succeed?

@mightyiam mightyiam left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread flake/dev/flake.lock
Comment on lines -96 to -116
"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"
}
},

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is one input less 🕺

Comment thread flake/dev/devshell.nix Outdated
{
imports = [
inputs.devshell.flakeModule
(inputs.devshell + "/flake-module.nix")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small price to pay?

Comment thread flake/dev/flake.nix Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nixpkgs will likely never have its own flake inputs, so I didn't bother.

Comment thread flake/dev/flake.nix Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flake-compat is also unlikely to have any of its own flake inputs, so I didn't bother.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flake-compat documents flake = false as its recommended usage.

Comment thread flake/dev/flake.nix
Comment on lines 34 to 38
nuschtosSearch = {
# TODO: newer versions fail with: TimeoutError: The operation was aborted due to timeout
url = "github:NuschtOS/search/b6f77b88e9009bfde28e2130e218e5123dc66796";
inputs.nixpkgs.follows = "dev-nixpkgs";
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread flake/dev/flake.nix Outdated
# keep-sorted start block=yes newline_separated=yes
devshell = {
url = "github:numtide/devshell";
inputs.nixpkgs.follows = "dev-nixpkgs";

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followses be gone!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tests/platforms/darwin.nix Outdated
{ config, ... }:
{
nixpkgs = {
source = self.inputs.dev-nixpkgs;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@MattSturgeon MattSturgeon May 15, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in main, the nix-darwin input has inputs.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.

Comment thread tests/platforms/darwin.nix Outdated
@mightyiam
mightyiam marked this pull request as ready for review May 15, 2026 07:10
Comment thread tests/platforms/darwin.nix Outdated
lib,
}:
self.inputs.nix-darwin.lib.darwinSystem {
(import (self.inputs.nix-darwin + "/eval-config.nix")) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth naming this function?

Suggested change
(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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, is it worth doing something like:

Suggested change
inherit (import (self.inputs.home-manager + "/lib") { inherit lib; })
hmLib = import (self.inputs.home-manager + "/lib") { inherit lib; };
inherit (hmLib)

?

Comment thread tests/platforms/hm-submodule-merge.nix Outdated
# 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; })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Comment thread tests/platforms/hm.nix Outdated
lib,
}:
self.inputs.home-manager.lib.homeManagerConfiguration {
(import (self.inputs.home-manager + "/lib") { inherit lib; }).homeManagerConfiguration {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

@MattSturgeon

Copy link
Copy Markdown
Member

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.

@MattSturgeon

Copy link
Copy Markdown
Member

By the way, I tried nix flake check in this repo and ran out of memory.
What's the trick to succeed?

I believe this is documented in CONTRIBUTING.md.

IIRC, we still have our entire test suite in the checks output, and when nix flake check tries to evaluate it all in one go it runs out of memory.

We have a tests command in our dev shell that runs the test suite via chunked evals. We also chunk them up in our ci.buildbot output, which you could build using something like nix-fast-build. We should probably look into dropping tests that can't be evaluated from the flake checks output.

@mightyiam
mightyiam force-pushed the dev-inputs-flake-false branch 2 times, most recently from 6c4c27f to d0b625b Compare May 15, 2026 15:31

@mightyiam mightyiam left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally get that this whole change does not affect users.

Well, here are some changes. Please let me know whether I missed anything.

Comment thread flake/dev/flake.nix
devshell = {
url = "github:numtide/devshell";
inputs.nixpkgs.follows = "dev-nixpkgs";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposes a vanilla /flake-module.nix, so we're good.

Comment thread flake/dev/flake.nix
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "dev-nixpkgs";
inputs.flake-compat.follows = "flake-compat";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposes a vanilla /flake-module.nix, so we're good.

Comment thread flake/dev/flake.nix
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "dev-nixpkgs";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provides a /lib/default.nix that takes { inherit lib; } so we're golden.

Comment thread flake/dev/flake.nix
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "dev-nixpkgs";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provides a vanilla /flake-module.nix so we're golden.

Comment thread flake/dev/flake.nix
Comment thread flake/dev/flake.nix
flake-compat.url = "github:NixOS/flake-compat";
flake-compat = {
url = "github:NixOS/flake-compat";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @MattSturgeon pointed, flake-compat instructs declaring it with flake=false in its readme.

Comment thread flake/dev/flake.nix Outdated
# keep-sorted start block=yes newline_separated=yes
devshell = {
url = "github:numtide/devshell";
inputs.nixpkgs.follows = "dev-nixpkgs";

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread flake/dev/flake.nix
nix-darwin = {
url = "github:lnl7/nix-darwin";
inputs.nixpkgs.follows = "dev-nixpkgs";
flake = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provides a vanilla /eval-config.nix so we're golden.

Comment thread tests/platforms/darwin.nix Outdated
{ config, ... }:
{
nixpkgs = {
source = self.inputs.dev-nixpkgs;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@MattSturgeon

MattSturgeon commented Jun 12, 2026 •

Copy link
Copy Markdown
Member

Closing as stale. Thanks for trying to improve things, regardless!

If you are motivated to rebase and re-open, just know that I'd want to see buy-in from other maintainers before committing to actually merge this refactor.

Plus, I'm unsure how strong the benefits are in doing this to our dev lockfile and I'm unconvinced we want to be "fighting" how flakes are intended to be used.

@mightyiam

Copy link
Copy Markdown
Member Author

Rebased and converted the import style from

(inputs.foo + "/module.nix")

to

"${inputs.foo}/module.nix"

inherit (self.inputs.home-manager.lib)
homeManagerConfiguration
;
hmLib = import (self.inputs.home-manager + "/lib") { inherit lib; };

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can get a little shorter by bumping home-manager:
nix-community/home-manager#9430

@mightyiam mightyiam left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is an improvement 🤷

@mightyiam

This comment was marked as outdated.

@MattSturgeon

This comment was marked as resolved.

@mightyiam

This comment was marked as resolved.

@MattSturgeon

This comment was marked as resolved.

@MattSturgeon MattSturgeon reopened this Jun 26, 2026
@mightyiam
mightyiam force-pushed the dev-inputs-flake-false branch from 4d35f4d to 51cee98 Compare June 26, 2026 14:52
@mightyiam

Copy link
Copy Markdown
Member Author

I'd be happy to rebase 🤷

@MattSturgeon

Copy link
Copy Markdown
Member

If you are motivated to rebase and re-open, just know that I'd want to see buy-in from other maintainers before committing to actually merge this refactor.

In 2 months, We've not seen any engagement from other maintainers. Either they're against the PR but keeping quiet or they're indifferent and don't wish to advocate for it.

Therefore I think we should re-close this and continue using flake inputs "as intended".

Plus, I'm unsure how strong the benefits are in doing this to our dev lockfile and I'm unconvinced we want to be "fighting" how flakes are intended to be used.

Honestly, the fact we have a separate "dev" lockfile at all is so that end users don't feel the need to "clean up" their downstream lockfiles by applyingfollows="" to our inputs.

In practice, the dev lockfile is only used when evaluating dev outputs, such as the devShell or checks; therefore I don't think it's necessary to over-optimise its dependency graph.

If upstream nix can implement lazier flake inputs fetching and/or avoid re-locking transitive inputs locally, then both issues will be solved upstream and we won't need either a separate dev lockfile or flake=false on inputs.

In the meantime, I'd rather reserve workarounds for where there is a tangible benefit to end users or a significant benefit to developers.


Sorry if I've strung you along by leaving the door open on this one.

@GaetanLepage

Copy link
Copy Markdown
Member

Hi!
This one slipped under my radar, sorry.
I agree with @MattSturgeon in the sense that benefits do not seem to be very limited.
Sorry for denying your contribution. I truly appreciate you taking the time to submit this PR.
If you feel like this is truly a mistake, please make your case.

All the best

@mightyiam

Copy link
Copy Markdown
Member Author

No worries, @GaetanLepage. Thank you for taking a look.

First, this is a low impact change, granted.

But I think the flake=false should obviously be preferred.

Let's take inputs.devshell.inputs.nixpkgs.follows = "nixvim/nixpkgs" as an example.
It prevents an extraneous input in the lockfile by deduplicating it with another.

inputs.devshell.flake=false acknowledges the truth, that that transitive input was never used anyway. And it guarantees that no new transitive inputs will be added for that input.

And what is the cost in this trade-off? Nothing? Is flake=false a dirty workaround? No, it's a clean way of saying "hey, I'm not using this input as a flake. Just fetch it for me, please".

This change is zero-cost and net-benefit.

@MattSturgeon

MattSturgeon commented Aug 16, 2026 •

Copy link
Copy Markdown
Member

Is flake=false a dirty workaround? No, it's a clean way of saying "hey, I'm not using this input as a flake. Just fetch it for me, please".

I agree with this in principle. It's why we fetch flake-compat with flake=false:1

flake-compat = {
url = "github:NixOS/flake-compat";
flake = false;
};

However, flake=false doesn't just affect transitive inputs; it tells Nix that the input is not a flake at all, meaning Nix won't evaluate its flake outputs either.
Therefore, the decision to use flake=false really comes down to whether we need or want an input's flake outputs, rather than just trying to avoid its transitive inputs.

In many cases, we actually do want to consume these inputs as flakes, because we prefer to use their flake outputs instead of importing from loose files. This is usually what I mean by consuming flakes "idiomatically."
Sometimes, flake outputs are the only "official" API endorsed by a project. Other times, it's just more convenient.

I'd fully support a PR that declares flake=false only on inputs where evaluating the flake provides zero benefit: i.e., where we don't use the flake outputs and we have no need for the transitive inputs.

I'd also be open to dropping unnecessary inputs altoghetr; for example we could switch from treefmt-nix to Nixpkgs's built-in treefmt.withConfig if we're willing to lose some of the pre-configured formatter defaults provided by treefmt-nix. Or we could drop devshell in favour of directly using mkShell (and maybe some flake apps outputs).

Footnotes

  1. The same applies to nix-systems, so I opened flake: mark systems as flake=false #4520. ↩

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants