Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 134942f56a7a
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 95bdfaa8bdac
Choose a head ref
  • 9 commits
  • 6 files changed
  • 1 contributor

Commits on May 31, 2019

  1. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    63c5c91 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    b971e40 View commit details
  3. Add operator << for LockFile

    Useful for debugging.
    edolstra committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    9169046 View commit details
  4. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    7adb10d View commit details
  5. Allow bare flakerefs as installables

    So now
    
      $ nix build blender-bin
    
    works and builds the default package from that flake. You don't need
    to add a colon at the end anymore.
    edolstra committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    ccb1bad View commit details
  6. Automatically determine subdir for path flakes

    This means that in a flake in a subdirectory of a Git repo, you can
    now do
    
      $ nix build
    
    rather than the inconvenient
    
      $ nix build ../..?dir=foo/bar
    edolstra committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    8abb864 View commit details
  7. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    8cb3bbd View commit details
  8. Doh

    edolstra committed May 31, 2019

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    15f2417 View commit details

Commits on Jun 3, 2019

  1. Merge pull request #2907 from NixOS/subdir

    Subdirectory improvements
    edolstra authored Jun 3, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    95bdfaa View commit details
Showing with 103 additions and 51 deletions.
  1. +13 −4 src/libexpr/primops/flake.cc
  2. +76 −45 src/libexpr/primops/flakeref.cc
  3. +5 −0 src/libexpr/primops/flakeref.hh
  4. +1 −1 src/libstore/build.cc
  5. +4 −0 src/nix/installables.cc
  6. +4 −1 tests/flakes.sh
17 changes: 13 additions & 4 deletions src/libexpr/primops/flake.cc
Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ nlohmann::json flakeEntryToJson(const LockFile::FlakeEntry & entry)
return json;
}

void writeLockFile(const LockFile & lockFile, const Path & path)
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
{
nlohmann::json json;
json["version"] = 1;
@@ -133,8 +133,14 @@ void writeLockFile(const LockFile & lockFile, const Path & path)
json["inputs"] = nlohmann::json::object();
for (auto & x : lockFile.flakeEntries)
json["inputs"][x.first.to_string()] = flakeEntryToJson(x.second);
stream << json.dump(4); // '4' = indentation in json file
return stream;
}

void writeLockFile(const LockFile & lockFile, const Path & path)
{
createDirs(dirOf(path));
writeFile(path, json.dump(4) + "\n"); // '4' = indentation in json file
writeFile(path, fmt("%s\n", lockFile));
}

Path getUserRegistryPath()
@@ -476,9 +482,12 @@ ResolvedFlake resolveFlake(EvalState & state, const FlakeRef & topRef, HandleLoc
Flake flake = getFlake(state, topRef, allowedToUseRegistries(handleLockFile, true));
LockFile oldLockFile;

if (!recreateLockFile (handleLockFile)) {
if (!recreateLockFile(handleLockFile)) {
// If recreateLockFile, start with an empty lockfile
oldLockFile = readLockFile(flake.sourceInfo.storePath + "/flake.lock"); // FIXME: symlink attack
// FIXME: symlink attack
oldLockFile = readLockFile(
state.store->toRealPath(flake.sourceInfo.storePath)
+ "/" + flake.sourceInfo.resolvedRef.subdir + "/flake.lock");
}

LockFile lockFile(oldLockFile);
121 changes: 76 additions & 45 deletions src/libexpr/primops/flakeref.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "flakeref.hh"
#include "store-api.hh"

#include <regex>

@@ -30,18 +31,14 @@ const static std::string schemeRegex = "(?:http|https|ssh|git|file)";
const static std::string authorityRegex = "[a-zA-Z0-9._~-]*";
const static std::string segmentRegex = "[a-zA-Z0-9._~-]+";
const static std::string pathRegex = "/?" + segmentRegex + "(?:/" + segmentRegex + ")*";
// FIXME: support escaping in query string.
// Note: '/' is not a valid query parameter, but so what...
const static std::string paramRegex = "[a-z]+=[/a-zA-Z0-9._-]*";
const static std::string paramsRegex = "(?:[?](" + paramRegex + "(?:&" + paramRegex + ")*))";

// 'dir' path elements cannot start with a '.'. We also reject
// potentially dangerous characters like ';'.
const static std::string subDirElemRegex = "(?:[a-zA-Z0-9_-]+[a-zA-Z0-9._-]*)";
const static std::string subDirRegex = subDirElemRegex + "(?:/" + subDirElemRegex + ")*";


FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
FlakeRef::FlakeRef(const std::string & uri_, bool allowRelative)
{
// FIXME: could combine this into one regex.

@@ -50,21 +47,46 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
std::regex::ECMAScript);

static std::regex githubRegex(
"github:(" + ownerRegex + ")/(" + repoRegex + ")(?:/" + revOrRefRegex + ")?"
+ paramsRegex + "?",
"github:(" + ownerRegex + ")/(" + repoRegex + ")(?:/" + revOrRefRegex + ")?",
std::regex::ECMAScript);

static std::regex uriRegex(
"((" + schemeRegex + "):" +
"(?://(" + authorityRegex + "))?" +
"(" + pathRegex + "))" +
paramsRegex + "?",
"(" + pathRegex + "))",
std::regex::ECMAScript);

static std::regex refRegex2(refRegex, std::regex::ECMAScript);

static std::regex subDirRegex2(subDirRegex, std::regex::ECMAScript);

auto [uri, params] = splitUriAndParams(uri_);

auto handleSubdir = [&](const std::string & name, const std::string & value) {
if (name == "dir") {
if (value != "" && !std::regex_match(value, subDirRegex2))
throw BadFlakeRef("flake '%s' has invalid subdirectory '%s'", uri, value);
subdir = value;
return true;
} else
return false;
};

auto handleGitParams = [&](const std::string & name, const std::string & value) {
if (name == "rev") {
if (!std::regex_match(value, revRegex))
throw BadFlakeRef("invalid Git revision '%s'", value);
rev = Hash(value, htSHA1);
} else if (name == "ref") {
if (!std::regex_match(value, refRegex2))
throw BadFlakeRef("invalid Git ref '%s'", value);
ref = value;
} else if (handleSubdir(name, value))
;
else return false;
return true;
};

std::cmatch match;
if (std::regex_match(uri.c_str(), match, flakeRegex)) {
IsAlias d;
@@ -88,17 +110,11 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
else if (match[4].matched) {
ref = match[4];
}
for (auto & param : tokenizeString<Strings>(match[5], "&")) {
auto n = param.find('=');
assert(n != param.npos);
std::string name(param, 0, n);
std::string value(param, n + 1);
if (name == "dir") {
if (value != "" && !std::regex_match(value, subDirRegex2))
throw Error("flake '%s' has invalid subdirectory '%s'", uri, value);
subdir = value;
} else
throw Error("invalid Git flake reference parameter '%s', in '%s'", name, uri);
for (auto & param : params) {
if (handleSubdir(param.first, param.second))
;
else
throw BadFlakeRef("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
}
data = d;
}
@@ -108,40 +124,44 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
{
IsGit d;
d.uri = match[1];
for (auto & param : tokenizeString<Strings>(match[5], "&")) {
auto n = param.find('=');
assert(n != param.npos);
std::string name(param, 0, n);
std::string value(param, n + 1);
if (name == "rev") {
if (!std::regex_match(value, revRegex))
throw Error("invalid Git revision '%s'", value);
rev = Hash(value, htSHA1);
} else if (name == "ref") {
if (!std::regex_match(value, refRegex2))
throw Error("invalid Git ref '%s'", value);
ref = value;
} else if (name == "dir") {
if (value != "" && !std::regex_match(value, subDirRegex2))
throw Error("flake '%s' has invalid subdirectory '%s'", uri, value);
subdir = value;
} else
for (auto & param : params) {
if (handleGitParams(param.first, param.second))
;
else
// FIXME: should probably pass through unknown parameters
throw Error("invalid Git flake reference parameter '%s', in '%s'", name, uri);
throw BadFlakeRef("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
}
if (rev && !ref)
throw Error("flake URI '%s' lacks a Git ref", uri);
throw BadFlakeRef("flake URI '%s' lacks a Git ref", uri);
data = d;
}

else if (hasPrefix(uri, "/") || (allowRelative && (hasPrefix(uri, "./") || hasPrefix(uri, "../") || uri == "."))) {
else if ((hasPrefix(uri, "/") || (allowRelative && (hasPrefix(uri, "./") || hasPrefix(uri, "../") || uri == ".")))
&& uri.find(':') == std::string::npos)
{
IsPath d;
d.path = allowRelative ? absPath(uri) : canonPath(uri);
if (allowRelative) {
d.path = absPath(uri);
while (true) {
if (pathExists(d.path + "/.git")) break;
subdir = baseNameOf(d.path) + (subdir.empty() ? "" : "/" + subdir);
d.path = dirOf(d.path);
if (d.path == "/")
throw BadFlakeRef("path '%s' does not reference a Git repository", uri);
}
} else
d.path = canonPath(uri);
data = d;
for (auto & param : params) {
if (handleGitParams(param.first, param.second))
;
else
throw BadFlakeRef("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
}
}

else
throw Error("'%s' is not a valid flake reference", uri);
throw BadFlakeRef("'%s' is not a valid flake reference", uri);
}

std::string FlakeRef::to_string() const
@@ -165,10 +185,10 @@ std::string FlakeRef::to_string() const
}

else if (auto refData = std::get_if<FlakeRef::IsPath>(&data)) {
assert(subdir == "");
string = refData->path;
if (ref) addParam("ref", *ref);
if (rev) addParam("rev", rev->gitRev());
return refData->path;
if (subdir != "") addParam("dir", subdir);
}

else if (auto refData = std::get_if<FlakeRef::IsGitHub>(&data)) {
@@ -217,4 +237,15 @@ FlakeRef FlakeRef::baseRef() const // Removes the ref and rev from a FlakeRef.
result.rev = std::nullopt;
return result;
}

std::optional<FlakeRef> parseFlakeRef(
const std::string & uri, bool allowRelative)
{
try {
return FlakeRef(uri, allowRelative);
} catch (BadFlakeRef & e) {
return {};
}
}

}
5 changes: 5 additions & 0 deletions src/libexpr/primops/flakeref.hh
Original file line number Diff line number Diff line change
@@ -180,4 +180,9 @@ struct FlakeRef

std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);

MakeError(BadFlakeRef, Error);

std::optional<FlakeRef> parseFlakeRef(
const std::string & uri, bool allowRelative = false);

}
2 changes: 1 addition & 1 deletion src/libstore/build.cc
Original file line number Diff line number Diff line change
@@ -3170,7 +3170,7 @@ void DerivationGoal::registerOutputs()
valid. */
delayedException = std::make_exception_ptr(
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
dest, h.to_string(), h2.to_string()));
dest, h.to_string(SRI), h2.to_string(SRI)));

Path actualDest = worker.store.toRealPath(dest);

4 changes: 4 additions & 0 deletions src/nix/installables.cc
Original file line number Diff line number Diff line change
@@ -314,6 +314,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
Strings{"packages." + std::string(s, 8)}));
}

else if (auto flakeRef = parseFlakeRef(s, true))
result.push_back(std::make_shared<InstallableFlake>(*this, std::move(*flakeRef),
getDefaultFlakeAttrPaths()));

else if ((colon = s.rfind(':')) != std::string::npos) {
auto flakeRef = std::string(s, 0, colon);
auto attrPath = std::string(s, colon + 1);
5 changes: 4 additions & 1 deletion tests/flakes.sh
Original file line number Diff line number Diff line change
@@ -131,9 +131,12 @@ nix build -o $TEST_ROOT/result --flake-registry $registry flake1:foo
[[ -e $TEST_ROOT/result/hello ]]

# Test defaultPackage.
nix build -o $TEST_ROOT/result --flake-registry $registry flake1:
nix build -o $TEST_ROOT/result --flake-registry $registry flake1
[[ -e $TEST_ROOT/result/hello ]]

nix build -o $TEST_ROOT/result --flake-registry $registry $flake1Dir
nix build -o $TEST_ROOT/result --flake-registry $registry file://$flake1Dir

# Building a flake with an unlocked dependency should fail in pure mode.
(! nix eval "(builtins.getFlake "$flake2Dir")")