Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for relative path inputs #10089

Open
wants to merge 18 commits into
base: master
Choose a base branch
from

Conversation

edolstra
Copy link
Member

@edolstra edolstra commented Feb 26, 2024

Motivation

We want it to be possible to refer to flake inputs that reside in the same repo via relative paths (e.g. inputs.foo.url = "path:./subdir"). Previously this didn't work very well because those inputs would be separately copied to the store, which is inefficient and makes references to parent directories tricky or impossible. Furthermore, they had their own NAR hash in the lock file, which is superfluous since the parent is already locked. In fact, if relative path inputs worked at all, it was by accident.

Now relative path inputs are accessed via the accessor of the calling flake. This avoids the unnecessary copy and makes it possible for relative path inputs to depend on flakes in a parent directory (so long as they're in the same tree).

Lock file nodes for relative path inputs now have a new parent field:

{
  "locked": {
    "path": "./subdir",
    "type": "path"
  },
  "original": {
    "path": "./subdir",
    "type": "path"
  },
  "parent": [
    "foo",
    "bar"
  ]
}

which denotes that ./subdir is to be interpreted relative to the directory of the bar input of the foo input of the root flake.

Extracted from the lazy-trees branch.

Context

Priorities and Process

Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@edolstra edolstra marked this pull request as draft February 26, 2024 18:53
@github-actions github-actions bot added documentation new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority fetching Networking with the outside (non-Nix) world, input locking labels Feb 26, 2024
Copy link

dpulls bot commented Mar 4, 2024

🎉 All dependencies have been resolved !

@thufschmitt thufschmitt added the idea approved The given proposal has been discussed and approved by the Nix team. An implementation is welcome. label Mar 5, 2024
@thufschmitt
Copy link
Member

Discussed during the Nix maintainers meeting on 2024-03-04.

  • Idea approved
  • Document that directly accessing .. in subflakes is likely to break in the future
  • Use path literals as the url for subflakes
  • Great overall
  • Right now, the PR allows import ../. in a subflake. Do we want that?
    • Directly accessing .. is convenient, but breaks the encapsulation property of flakes
    • We can however access other parts of the tree with relative inputs (that can go up until the root)
      • inputs.something_else = ../foo
    • From an implementation perspective, hard to forbid without lazy trees
    • Decision:
      • Don't formally forbid direct access to .. since it's hard without lazy trees
      • Once lazy trees lands, make that conditional on a flag
      • Potentially make the default restrictive. Would be a breaking change, but
        • can be mitigated by only making that change effective after a flake update
        • small breakage as getting the old behavior back would only be a one-line change
  • Syntax for relative flakes?
    • Currently reuses url = "path:./foo" that already kinda works on master
    • A more natural syntax would be url = ./foo
      • This is actually what most people seem to try first when they want subflakes

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/2024-03-04-nix-team-meeting-minute-130/40830/1

edolstra added a commit that referenced this pull request Mar 7, 2024
This wasn't caught by CI because #10149 and #10152 pass
individually... It doesn't happen on lazy-trees either because we
never try to fetch relative path flakes (#10089).
edolstra added a commit to edolstra/nix that referenced this pull request Apr 15, 2024
…re path

This is needed for the path:// input scheme (until it returns a
FSInputAccessor to the original path, but that's blocked by NixOS#10089)
and the Mercurial input scheme.
edolstra added a commit to edolstra/nix that referenced this pull request Apr 19, 2024
…re path

This is needed for the path:// input scheme (until it returns a
FSInputAccessor to the original path, but that's blocked by NixOS#10089)
and the Mercurial input scheme.
Subflakes are flakes in the same tree, accessed in flake inputs via
relative paths (e.g. `inputs.foo.url = "path:./subdir"`). Previously
these didn't work very well because they would be separately copied to
the store, which is inefficient and makes references to parent
directories tricky or impossible. Furthermore, they had their own NAR
hash in the lock file, which is superfluous since the parent is
already locked.

Now subflakes are accessed via the accessor of the calling flake. This
avoids the unnecessary copy and makes it possible for subflakes to
depend on flakes in a parent directory (so long as they're in the same
tree).

Lock file nodes for relative flake inputs now have a new `parent` field:

  {
    "locked": {
      "path": "./subdir",
      "type": "path"
    },
    "original": {
      "path": "./subdir",
      "type": "path"
    },
    "parent": [
      "foo",
      "bar"
    ]
  }

which denotes that `./subdir` is to be interpreted relative to the
directory of the `bar` input of the `foo` input of the root flake.

Extracted from the lazy-trees branch.
edolstra added 2 commits May 17, 2024 16:38
`parentNode.sourceInfo.outPath` does not include the subdir of the
parent flake, while `parentNode.outPath` does. So we need to use the
latter.
edolstra added a commit to edolstra/nix that referenced this pull request Sep 11, 2024
Relative path flakes ("subflakes") are basically fundamentally
broken, since they produce lock file entries like

  "locked": {
    "lastModified": 1,
    "narHash": "sha256-/2tW9SKjQbRLzfcJs5SHijli6l3+iPr1235zylGynK8=",
    "path": "./flakeC",
    "type": "path"
  },

that don't specify what "./flakeC" is relative to. They *sometimes*
worked by accident because the `narHash` field allowed
`fetchToStore()` to get the store path of the subflake *if* it
happened to exist in the local store or in a substituter.

Subflakes are properly fixed in NixOS#10089 (which adds a "parent" field to
the lock file). Rather than come up with some crazy hack to make them
work in the interim, let's just disable the only test that depends on
the broken behaviour for now.
@edolstra edolstra marked this pull request as ready for review November 27, 2024 14:25
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/2024-11-27-nix-team-meeting-minutes-198/56691/1

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/2024-12-18-nix-team-meeting-minutes-204/57602/1

@MagicRB
Copy link
Contributor

MagicRB commented Dec 19, 2024

Does this also mean that subflakes will auto-update? In other words, nix flake update subflake ought to be unnecessary with this right?

Copy link
Member

@roberth roberth left a comment

Choose a reason for hiding this comment

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

Reviewed together in meeting

  • Release note: Generalizing the flake format can break tools that consume flake.nix files. For instance toJSON (import ./flake.nix).inputs will not work as expected anymore for flakes that use path values.

InputPath -> InputChain?

* Only for relative path flakes, i.e. 'path:./foo', returns the
* relative path, i.e. './foo'.
*/
std::optional<std::string> isRelative() const;
Copy link
Member

Choose a reason for hiding this comment

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

This should be in the expr / flake layer.
Open issue?

src/nix/flake.md Outdated Show resolved Hide resolved
src/libflake/flake/flakeref.hh Outdated Show resolved Hide resolved
src/libflake/flake/flake.cc Outdated Show resolved Hide resolved
/* Respect the "flakeness" of the input even if we
override it. */
if (hasOverride)
input.isFlake = input2.isFlake;
Copy link
Member

Choose a reason for hiding this comment

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

Off-topic: perhaps flake-ness should be "type-checked"

(existing code, moved)

src/libflake/flake/flakeref.cc Show resolved Hide resolved
@@ -38,11 +38,19 @@ struct LockedNode : Node
FlakeRef lockedRef, originalRef;
bool isFlake = true;

/* The node relative to which relative source paths
(e.g. 'path:../foo') are interpreted. */
std::optional<InputPath> parentPath;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
std::optional<InputPath> parentPath;
std::optional<InputPath> parentInputChain;

as mentioned in the review header comment

Copy link
Member Author

Choose a reason for hiding this comment

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

For consistency, let's postpone this to a future PR that resolves #12098.

src/nix/flake.md Show resolved Hide resolved
tests/functional/flakes/relative-paths.sh Show resolved Hide resolved
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/2024-12-23-nix-team-meeting-minutes-205/57783/1

@tomberek
Copy link
Contributor

(using: nix-2.26.0pre20241223_9223d64) An update to the input of a subflake does not get automatically picked up by the root when running a build.

  1. Root and child are sync'd.
  2. Parent uses path:./subdir to point to child and re-export its outputs.
  3. Child has an input to a third, remote-flake.
  4. Remote flake has a new commit pushed.
  5. Child is updated with nix flake update in the subdir.
  6. Run a build in the parent.

This will run the root with the "old" child and the mismatch/update is not detected. At this point the flake:root/subdir/src and flake:subdir/src are locked to different revisions.

  1. Run nix flake update in the root.
  2. Lock in the root now points to the same sources as the child.

Not sure if this is intended,,, but was a bit surprising. I'd guess that the root flake would detect the mismatch between its lock and that of the child - similar to how a significant change to an input is detected and locks updated when running things like nix build.

@roberth
Copy link
Member

roberth commented Dec 26, 2024 via email

@tomberek
Copy link
Contributor

I still need to more thoroughly test it, but I'm afraid the unsync'd lockfiles is a problem. When evaluating the root flake with an outdated/updated/modified lockfile, it will still use the flake.nix from the current checkout via path:./subdir, but not the subdir/flake.lock, instead using the inputs from ./flake.lock. It would also be weird to have an old subdir/flake.nix be used - user keeps editing and doesn't see the changes.

Not sure if this is intended. Idea brainstorming:

  • nix flake update --recursive
  • detect de-sync and provide warning
  • do not store the subflake's lock information, just a pointer to the other flake.lock
  • document "multi-flake" vs "subflake".

note: I do love the new capability to avoid the "infinite updating lockfile"! Excited to keep playing with it.

@roberth
Copy link
Member

roberth commented Dec 31, 2024

At this point the flake:root/subdir/src and flake:subdir/src are locked to different revisions.

I think I'm reading pseudo-syntax that's a little ambiguous, but I would think that the locking of the local sources is fixed by this PR.

The locking of transitive dependencies between flakes in the same repo I think should be solved with #7730; otherwise we'll need a lot of new logic to deal with its symptoms.

@roberth roberth changed the title Improve support for subflakes Improve support for relative path inputs Dec 31, 2024
@roberth roberth added the flakes label Dec 31, 2024
@edolstra
Copy link
Member Author

edolstra commented Jan 7, 2025

@tomberek

An update to the input of a subflake does not get automatically picked up by the root when running a build.

That's right, and probably not ideal, but it's consistent with how lock files currently work in general: lock files are independent, and the only time that we use the lock file of an input is if the referring flake doesn't have a lock for that input yet. So a change to the lock file of an input doesn't propagate to the referring flake.

This should indeed be fixed by having sparse lock files (#7730).

@roberth

"Subflake" suggests some sort of directional relationship other than just an input. We should avoid that term in the release notes, and/or explain what's not implemented.

Yeah we don't use the term "subflake" anywhere (except previously in the PR description, fixed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation fetching Networking with the outside (non-Nix) world, input locking flakes idea approved The given proposal has been discussed and approved by the Nix team. An implementation is welcome. new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority
Projects
Status: ⚖ To discuss
Development

Successfully merging this pull request may close these issues.

Allow flakes to refer to other flakes by relative path
6 participants