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

Merged
merged 25 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b2be6fe
Improve support for subflakes
edolstra Apr 23, 2024
49f592d
call-flake.nix: Fix relative path resolution
edolstra May 17, 2024
3180671
Allow the 'url' flake input attribute to be a path literal
edolstra May 17, 2024
21fc07c
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Sep 16, 2024
09d7197
shellcheck
edolstra Sep 16, 2024
71d4bb8
parentPath -> parentInputPath
edolstra Sep 16, 2024
91e7d49
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Sep 23, 2024
f206325
tests/functional/flakes/relative-paths.sh: Fix build failure in hydra…
edolstra Sep 25, 2024
0b00bf7
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Nov 22, 2024
00b99b8
Remove FIXME
edolstra Nov 22, 2024
985b2f9
Remove FIXME
edolstra Nov 27, 2024
8534c42
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Dec 18, 2024
9223d64
Remove dead code
edolstra Dec 23, 2024
75cda2d
Document path values in inputs
edolstra Jan 7, 2025
e8c7dd9
Rename allowRelative -> preserveRelativePaths
edolstra Jan 7, 2025
0792152
Rename Override -> OverrideTarget
edolstra Jan 7, 2025
ef2739b
Example of referencing parent directories
edolstra Jan 7, 2025
d329b26
Fix manual
edolstra Jan 7, 2025
cd0127f
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Jan 13, 2025
6cc5b48
Add release note
edolstra Jan 14, 2025
550fe88
Merge remote-tracking branch 'origin/master' into relative-flakes
edolstra Jan 16, 2025
521667e
Fix follow-paths test
edolstra Jan 16, 2025
5d03ef9
PathInputSchema::getAbsPath(): Return std::filesystem::path
edolstra Jan 16, 2025
8b1fb92
flakes.md: Fix indentation that broke the list
edolstra Jan 16, 2025
db46d40
Update release note
edolstra Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/manual/rl-next/relative-path-flakes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
synopsis: "Support for relative path inputs"
prs: [10089]
---

Flakes can now refer to other flakes in the same repository using relative paths, e.g.
```nix
inputs.foo.url = "path:./foo";
```
uses the flake in the `foo` subdirectory of the referring flake. For more information, see the documentation on [the `path` flake input type](@docroot@/command-ref/new-cli/nix3-flake.md#path-fetcher).

This feature required a change to the lock file format. Previous Nix versions will not be able to use lock files that have locks for relative path inputs in them.
7 changes: 7 additions & 0 deletions src/libexpr/call-flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ let
(key: node:
let

parentNode = allNodes.${getInputByPath lockFile.root node.parent};

sourceInfo =
if overrides ? ${key}
then
overrides.${key}.sourceInfo
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/"
then
parentNode.sourceInfo // {
outPath = parentNode.outPath + ("/" + node.locked.path);
}
else
# FIXME: remove obsolete node.info.
# Note: lock file entries are always final.
Expand Down
6 changes: 6 additions & 0 deletions src/libfetchers/fetchers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ bool Input::isFinal() const
return maybeGetBoolAttr(attrs, "__final").value_or(false);
}

std::optional<std::string> Input::isRelative() const
{
assert(scheme);
return scheme->isRelative(*this);
}

Attrs Input::toAttrs() const
{
return attrs;
Expand Down
14 changes: 9 additions & 5 deletions src/libfetchers/fetchers.hh
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ struct Input
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;

/**
* path of the parent of this input, used for relative path resolution
*/
std::optional<Path> parent;

/**
* Cached result of getFingerprint().
*/
Expand Down Expand Up @@ -104,6 +99,12 @@ public:
bool isConsideredLocked(
const Settings & settings) const;

/**
* 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?


/**
* Return whether this is a "final" input, meaning that fetching
* it will not add, remove or change any attributes. (See
Expand Down Expand Up @@ -269,6 +270,9 @@ struct InputScheme

virtual bool isLocked(const Input & input) const
{ return false; }

virtual std::optional<std::string> isRelative(const Input & input) const
{ return std::nullopt; }
};

void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
Expand Down
37 changes: 10 additions & 27 deletions src/libfetchers/path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ struct PathInputScheme : InputScheme
std::string_view contents,
std::optional<std::string> commitMsg) const override
{
writeFile((CanonPath(getAbsPath(input)) / path).abs(), contents);
writeFile(getAbsPath(input) / path.rel(), contents);
}

std::optional<std::string> isRelative(const Input & input) const
std::optional<std::string> isRelative(const Input & input) const override
{
auto path = getStrAttr(input.attrs, "path");
if (isAbsolute(path))
Expand All @@ -108,44 +108,27 @@ struct PathInputScheme : InputScheme
return (bool) input.getNarHash();
}

CanonPath getAbsPath(const Input & input) const
std::filesystem::path getAbsPath(const Input & input) const
{
auto path = getStrAttr(input.attrs, "path");

if (path[0] == '/')
return CanonPath(path);
if (isAbsolute(path))
return canonPath(path);

throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
}

std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
{
Input input(_input);
std::string absPath;
auto path = getStrAttr(input.attrs, "path");

if (path[0] != '/') {
if (!input.parent)
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());

auto parent = canonPath(*input.parent);

// the path isn't relative, prefix it
absPath = nix::absPath(path, parent);

// for security, ensure that if the parent is a store path, it's inside it
if (store->isInStore(parent)) {
auto storePath = store->printStorePath(store->toStorePath(parent).first);
if (!isDirOrInDir(absPath, storePath))
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
}
} else
absPath = path;
auto absPath = getAbsPath(input);
edolstra marked this conversation as resolved.
Show resolved Hide resolved

Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to the store", absPath));

// FIXME: check whether access to 'path' is allowed.
auto storePath = store->maybeParseStorePath(absPath);
auto storePath = store->maybeParseStorePath(absPath.string());

if (storePath)
store->addTempRoot(*storePath);
Expand All @@ -154,7 +137,7 @@ struct PathInputScheme : InputScheme
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
// FIXME: try to substitute storePath.
auto src = sinkToSource([&](Sink & sink) {
mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter);
});
storePath = store->addToStoreFromDump(*src, "source");
}
Expand All @@ -176,7 +159,7 @@ struct PathInputScheme : InputScheme
store object and the subpath. */
auto path = getAbsPath(input);
try {
auto [storePath, subPath] = store->toStorePath(path.abs());
auto [storePath, subPath] = store->toStorePath(path.string());
auto info = store->queryPathInfo(storePath);
return fmt("path:%s:%s", info->narHash.to_string(HashFormat::Base16, false), subPath);
} catch (Error &) {
Expand Down
Loading
Loading