Merge pull request #5771 from edolstra/single-file-flake-inputs

Re-allow inputs.x.url = "/path/to/file"
This commit is contained in:
Théophane Hufschmitt 2021-12-14 09:17:24 +01:00 committed by GitHub
commit 99d617bcde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 40 deletions

View file

@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
if (!attrs.empty()) if (!attrs.empty())
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos); throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
if (url) if (url)
input.ref = parseFlakeRef(*url, baseDir, true); input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
} }
if (!input.follows && !input.ref) if (!input.follows && !input.ref)
@ -194,8 +194,8 @@ static Flake getFlake(
state, originalRef, allowLookup, flakeCache); state, originalRef, allowLookup, flakeCache);
// Guard against symlink attacks. // Guard against symlink attacks.
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir); auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
auto flakeFile = canonPath(flakeDir + "/flake.nix"); auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
if (!isInDir(flakeFile, sourceInfo.actualPath)) if (!isInDir(flakeFile, sourceInfo.actualPath))
throw Error("'flake.nix' file of flake '%s' escapes from '%s'", throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
lockedRef, state.store->printStorePath(sourceInfo.storePath)); lockedRef, state.store->printStorePath(sourceInfo.storePath));
@ -570,7 +570,7 @@ LockedFlake lockFlake(
}; };
// Bring in the current ref for relative path resolution if we have it // Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir); auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks( computeLocks(
flake.inputs, newLockFile.root, {}, flake.inputs, newLockFile.root, {},

View file

@ -48,9 +48,12 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
} }
FlakeRef parseFlakeRef( FlakeRef parseFlakeRef(
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing) const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
{ {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing); auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
if (fragment != "") if (fragment != "")
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
return flakeRef; return flakeRef;
@ -67,7 +70,10 @@ std::optional<FlakeRef> maybeParseFlakeRef(
} }
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing) const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
{ {
using namespace fetchers; using namespace fetchers;
@ -112,46 +118,49 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */ repo (i.e. the directory containing .git). */
path = absPath(path, baseDir, true); path = absPath(path, baseDir);
if (!S_ISDIR(lstat(path).st_mode)) if (isFlake) {
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
if (!allowMissing && !pathExists(path + "/flake.nix")) if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
auto flakeRoot = path; if (!allowMissing && !pathExists(path + "/flake.nix"))
std::string subdir; throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
while (flakeRoot != "/") { auto flakeRoot = path;
if (pathExists(flakeRoot + "/.git")) { std::string subdir;
auto base = std::string("git+file://") + flakeRoot;
auto parsedURL = ParsedURL{ while (flakeRoot != "/") {
.url = base, // FIXME if (pathExists(flakeRoot + "/.git")) {
.base = base, auto base = std::string("git+file://") + flakeRoot;
.scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = decodeQuery(match[2]),
};
if (subdir != "") { auto parsedURL = ParsedURL{
if (parsedURL.query.count("dir")) .url = base, // FIXME
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); .base = base,
parsedURL.query.insert_or_assign("dir", subdir); .scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = decodeQuery(match[2]),
};
if (subdir != "") {
if (parsedURL.query.count("dir"))
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
parsedURL.query.insert_or_assign("dir", subdir);
}
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
fragment);
} }
if (pathExists(flakeRoot + "/.git/shallow")) subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
parsedURL.query.insert_or_assign("shallow", "1"); flakeRoot = dirOf(flakeRoot);
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
fragment);
} }
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
} }
} else { } else {

View file

@ -62,13 +62,19 @@ struct FlakeRef
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
FlakeRef parseFlakeRef( FlakeRef parseFlakeRef(
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false); const std::string & url,
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true);
std::optional<FlakeRef> maybeParseFlake( std::optional<FlakeRef> maybeParseFlake(
const std::string & url, const std::optional<Path> & baseDir = {}); const std::string & url, const std::optional<Path> & baseDir = {});
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false); const std::string & url,
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true);
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment( std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir = {}); const std::string & url, const std::optional<Path> & baseDir = {});

View file

@ -249,6 +249,14 @@ cat > $flake3Dir/flake.nix <<EOF
url = git+file://$nonFlakeDir; url = git+file://$nonFlakeDir;
flake = false; flake = false;
}; };
nonFlakeFile = {
url = path://$nonFlakeDir/README.md;
flake = false;
};
nonFlakeFile2 = {
url = "$nonFlakeDir/README.md";
flake = false;
};
}; };
description = "Fnord"; description = "Fnord";
@ -265,6 +273,8 @@ cat > $flake3Dir/flake.nix <<EOF
dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix"); dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix");
buildCommand = '' buildCommand = ''
cat \${inputs.nonFlake}/README.md > \$out cat \${inputs.nonFlake}/README.md > \$out
[[ \$(cat \${inputs.nonFlake}/README.md) = \$(cat \${inputs.nonFlakeFile}) ]]
[[ \${inputs.nonFlakeFile} = \${inputs.nonFlakeFile2} ]]
''; '';
}; };
}; };