aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/backport.yml2
-rw-r--r--doc/manual/src/release-notes/rl-next.md6
-rw-r--r--src/libexpr/eval.cc36
-rw-r--r--src/libexpr/eval.hh13
-rw-r--r--src/libexpr/flake/call-flake.nix20
-rw-r--r--src/libexpr/primops/fetchTree.cc12
-rw-r--r--src/libstore/globals.hh11
-rw-r--r--src/libutil/tests/url.cc33
-rw-r--r--src/libutil/url.cc17
-rw-r--r--src/libutil/url.hh1
-rw-r--r--tests/fetchGit.sh14
-rw-r--r--tests/flakes/inputs.sh80
-rw-r--r--tests/local.mk1
-rw-r--r--tests/nix_path.sh5
-rw-r--r--tests/restricted.sh3
15 files changed, 204 insertions, 50 deletions
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 558cfa804..b04723b95 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
- uses: zeebe-io/backport-action@v1.1.0
+ uses: zeebe-io/backport-action@v1.2.0
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index 7e8344e63..a38305fde 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -3,6 +3,12 @@
* A new function `builtins.readFileType` is available. It is similar to
`builtins.readDir` but acts on a single file or directory.
+* In flakes, the `.outPath` attribute of a flake now always refers to the
+ directory containing the `flake.nix`. This was not the case for when
+ `flake.nix` was in a subdirectory of e.g. a git repository.
+ The root of the source of a flake in a subdirectory is still available in
+ `.sourceInfo.outPath`.
+
* The `builtins.readDir` function has been optimized when encountering not-yet-known
file types from POSIX's `readdir`. In such cases the type of each file is/was
discovered by making multiple syscalls. This change makes these operations
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 3e37c7f60..21fc4d0fe 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -519,7 +519,6 @@ EvalState::EvalState(
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
/* Initialise the Nix expression search path. */
- evalSettings.nixPath.setDefault(evalSettings.getDefaultNixPath());
if (!evalSettings.pureEval) {
for (auto & i : _searchPath) addToSearchPath(i);
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i);
@@ -2473,35 +2472,30 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
EvalSettings::EvalSettings()
{
+ auto var = getEnv("NIX_PATH");
+ if (var) nixPath = parseNixPath(*var);
}
-/* impure => NIX_PATH or a default path
- * restrict-eval => NIX_PATH
- * pure-eval => empty
- */
Strings EvalSettings::getDefaultNixPath()
{
- if (pureEval)
- return {};
-
- auto var = getEnv("NIX_PATH");
- if (var) {
- return parseNixPath(*var);
- } else if (restrictEval) {
- return {};
- } else {
- Strings res;
- auto add = [&](const Path & p, const std::optional<std::string> & s = std::nullopt) {
- if (pathExists(p))
- res.push_back(s ? *s + "=" + p : p);
- };
+ Strings res;
+ auto add = [&](const Path & p, const std::string & s = std::string()) {
+ if (pathExists(p)) {
+ if (s.empty()) {
+ res.push_back(p);
+ } else {
+ res.push_back(s + "=" + p);
+ }
+ }
+ };
+ if (!evalSettings.restrictEval && !evalSettings.pureEval) {
add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels");
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
add(settings.nixStateDir + "/profiles/per-user/root/channels");
-
- return res;
}
+
+ return res;
}
bool EvalSettings::isPseudoUrl(std::string_view s)
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 2340ef67b..e4d5906bd 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -570,7 +570,7 @@ struct EvalSettings : Config
{
EvalSettings();
- Strings getDefaultNixPath();
+ static Strings getDefaultNixPath();
static bool isPseudoUrl(std::string_view s);
@@ -580,15 +580,8 @@ struct EvalSettings : Config
"Whether builtin functions that allow executing native code should be enabled."};
Setting<Strings> nixPath{
- this, {}, "nix-path",
- R"(
- List of directories to be searched for `<...>` file references.
-
- If [pure evaluation](#conf-pure-eval) is disabled,
- this is initialised using the [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH)
- environment variable, or, if it is unset and [restricted evaluation](#conf-restrict-eval)
- is disabled, a default search path including the user's and `root`'s channels.
- )"};
+ this, getDefaultNixPath(), "nix-path",
+ "List of directories to be searched for `<...>` file references."};
Setting<bool> restrictEval{
this, false, "restrict-eval",
diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/flake/call-flake.nix
index 8061db3df..4beb0b0fe 100644
--- a/src/libexpr/flake/call-flake.nix
+++ b/src/libexpr/flake/call-flake.nix
@@ -16,7 +16,9 @@ let
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
- flake = import (sourceInfo + (if subdir != "" then "/" else "") + subdir + "/flake.nix");
+ outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir);
+
+ flake = import (outPath + "/flake.nix");
inputs = builtins.mapAttrs
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
@@ -43,7 +45,21 @@ let
outputs = flake.outputs (inputs // { self = result; });
- result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake"; };
+ result =
+ outputs
+ # We add the sourceInfo attribute for its metadata, as they are
+ # relevant metadata for the flake. However, the outPath of the
+ # sourceInfo does not necessarily match the outPath of the flake,
+ # as the flake may be in a subdirectory of a source.
+ # This is shadowed in the next //
+ // sourceInfo
+ // {
+ # This shadows the sourceInfo.outPath
+ inherit outPath;
+
+ inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake";
+ };
+
in
if node.flake or true then
assert builtins.isFunction flake.outputs;
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 2e924c302..c9faf3ffb 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -4,6 +4,7 @@
#include "fetchers.hh"
#include "filetransfer.hh"
#include "registry.hh"
+#include "url.hh"
#include <ctime>
#include <iomanip>
@@ -68,7 +69,16 @@ void emitTreeAttrs(
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
{
state.checkURI(uri);
- return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
+ if (uri.find("://") == std::string::npos) {
+ const auto p = ParsedURL {
+ .scheme = defaultScheme,
+ .authority = "",
+ .path = uri
+ };
+ return p.to_string();
+ } else {
+ return uri;
+ }
}
std::string fixURIForGit(std::string uri, EvalState & state)
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 0a4912f67..93086eaf8 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -201,7 +201,16 @@ public:
{"build-timeout"}};
PathSetting buildHook{this, true, "", "build-hook",
- "The path of the helper program that executes builds to remote machines."};
+ R"(
+ The path to the helper program that executes remote builds.
+
+ Nix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon.
+ The default value is the internal Nix binary that implements remote building.
+
+ > **Important**
+ >
+ > Change this setting only if you really know what you’re doing.
+ )"};
Setting<std::string> builders{
this, "@" + nixConfDir + "/machines", "builders",
diff --git a/src/libutil/tests/url.cc b/src/libutil/tests/url.cc
index e0c438b4d..a908631e6 100644
--- a/src/libutil/tests/url.cc
+++ b/src/libutil/tests/url.cc
@@ -302,4 +302,37 @@ namespace nix {
ASSERT_EQ(d, s);
}
+
+ /* ----------------------------------------------------------------------------
+ * percentEncode
+ * --------------------------------------------------------------------------*/
+
+ TEST(percentEncode, encodesUrlEncodedString) {
+ std::string s = percentEncode("==@==");
+ std::string d = "%3D%3D%40%3D%3D";
+ ASSERT_EQ(d, s);
+ }
+
+ TEST(percentEncode, keepArgument) {
+ std::string a = percentEncode("abd / def");
+ std::string b = percentEncode("abd / def", "/");
+ ASSERT_EQ(a, "abd%20%2F%20def");
+ ASSERT_EQ(b, "abd%20/%20def");
+ }
+
+ TEST(percentEncode, inverseOfDecode) {
+ std::string original = "%3D%3D%40%3D%3D";
+ std::string once = percentEncode(original);
+ std::string back = percentDecode(once);
+
+ ASSERT_EQ(back, original);
+ }
+
+ TEST(percentEncode, trailingPercent) {
+ std::string s = percentEncode("==@==%");
+ std::string d = "%3D%3D%40%3D%3D%25";
+
+ ASSERT_EQ(d, s);
+ }
+
}
diff --git a/src/libutil/url.cc b/src/libutil/url.cc
index 4e43455e1..9e44241ac 100644
--- a/src/libutil/url.cc
+++ b/src/libutil/url.cc
@@ -88,17 +88,22 @@ std::map<std::string, std::string> decodeQuery(const std::string & query)
return result;
}
-std::string percentEncode(std::string_view s)
+const static std::string allowedInQuery = ":@/?";
+const static std::string allowedInPath = ":@/";
+
+std::string percentEncode(std::string_view s, std::string_view keep)
{
std::string res;
for (auto & c : s)
+ // unreserved + keep
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
- || strchr("-._~!$&'()*+,;=:@", c))
+ || strchr("-._~", c)
+ || keep.find(c) != std::string::npos)
res += c;
else
- res += fmt("%%%02x", (unsigned int) c);
+ res += fmt("%%%02X", (unsigned int) c);
return res;
}
@@ -109,9 +114,9 @@ std::string encodeQuery(const std::map<std::string, std::string> & ss)
for (auto & [name, value] : ss) {
if (!first) res += '&';
first = false;
- res += percentEncode(name);
+ res += percentEncode(name, allowedInQuery);
res += '=';
- res += percentEncode(value);
+ res += percentEncode(value, allowedInQuery);
}
return res;
}
@@ -122,7 +127,7 @@ std::string ParsedURL::to_string() const
scheme
+ ":"
+ (authority ? "//" + *authority : "")
- + path
+ + percentEncode(path, allowedInPath)
+ (query.empty() ? "" : "?" + encodeQuery(query))
+ (fragment.empty() ? "" : "#" + percentEncode(fragment));
}
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index 2a9fb34c1..ddd673d65 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -22,6 +22,7 @@ struct ParsedURL
MakeError(BadURL, Error);
std::string percentDecode(std::string_view in);
+std::string percentEncode(std::string_view s, std::string_view keep="");
std::map<std::string, std::string> decodeQuery(const std::string & query);
diff --git a/tests/fetchGit.sh b/tests/fetchGit.sh
index da09c3f37..a7a8df186 100644
--- a/tests/fetchGit.sh
+++ b/tests/fetchGit.sh
@@ -237,3 +237,17 @@ rm -rf $repo/.git
# should succeed for a repo without commits
git init $repo
path10=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath")
+
+# should succeed for a path with a space
+# regression test for #7707
+repo="$TEST_ROOT/a b"
+git init "$repo"
+git -C "$repo" config user.email "foobar@example.com"
+git -C "$repo" config user.name "Foobar"
+
+echo utrecht > "$repo/hello"
+touch "$repo/.gitignore"
+git -C "$repo" add hello .gitignore
+git -C "$repo" commit -m 'Bla1'
+cd "$repo"
+path11=$(nix eval --impure --raw --expr "(builtins.fetchGit ./.).outPath")
diff --git a/tests/flakes/inputs.sh b/tests/flakes/inputs.sh
new file mode 100644
index 000000000..80620488a
--- /dev/null
+++ b/tests/flakes/inputs.sh
@@ -0,0 +1,80 @@
+source ./common.sh
+
+requireGit
+
+
+test_subdir_self_path() {
+ baseDir=$TEST_ROOT/$RANDOM
+ flakeDir=$baseDir/b-low
+ mkdir -p $flakeDir
+ writeSimpleFlake $baseDir
+ writeSimpleFlake $flakeDir
+
+ echo all good > $flakeDir/message
+ cat > $flakeDir/flake.nix <<EOF
+{
+ outputs = inputs: rec {
+ packages.$system = rec {
+ default =
+ assert builtins.readFile ./message == "all good\n";
+ assert builtins.readFile (inputs.self + "/message") == "all good\n";
+ import ./simple.nix;
+ };
+ };
+}
+EOF
+ (
+ nix build $baseDir?dir=b-low --no-link
+ )
+}
+test_subdir_self_path
+
+
+test_git_subdir_self_path() {
+ repoDir=$TEST_ROOT/repo-$RANDOM
+ createGitRepo $repoDir
+ flakeDir=$repoDir/b-low
+ mkdir -p $flakeDir
+ writeSimpleFlake $repoDir
+ writeSimpleFlake $flakeDir
+
+ echo all good > $flakeDir/message
+ cat > $flakeDir/flake.nix <<EOF
+{
+ outputs = inputs: rec {
+ packages.$system = rec {
+ default =
+ assert builtins.readFile ./message == "all good\n";
+ assert builtins.readFile (inputs.self + "/message") == "all good\n";
+ assert inputs.self.outPath == inputs.self.sourceInfo.outPath + "/b-low";
+ import ./simple.nix;
+ };
+ };
+}
+EOF
+ (
+ cd $flakeDir
+ git add .
+ git commit -m init
+ # nix build
+ )
+
+ clientDir=$TEST_ROOT/client-$RANDOM
+ mkdir -p $clientDir
+ cat > $clientDir/flake.nix <<EOF
+{
+ inputs.inp = {
+ type = "git";
+ url = "file://$repoDir";
+ dir = "b-low";
+ };
+
+ outputs = inputs: rec {
+ packages = inputs.inp.packages;
+ };
+}
+EOF
+ nix build $clientDir --no-link
+
+}
+test_git_subdir_self_path
diff --git a/tests/local.mk b/tests/local.mk
index a4537cf09..4a620f18b 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -5,6 +5,7 @@ nix_tests = \
flakes/mercurial.sh \
flakes/circular.sh \
flakes/init.sh \
+ flakes/inputs.sh \
flakes/follow-paths.sh \
flakes/bundle.sh \
flakes/check.sh \
diff --git a/tests/nix_path.sh b/tests/nix_path.sh
index d16fb4bb2..2b222b4a1 100644
--- a/tests/nix_path.sh
+++ b/tests/nix_path.sh
@@ -12,8 +12,3 @@ nix-instantiate --eval -E '<by-relative-path/simple.nix>' --restrict-eval
[[ $(nix-instantiate --find-file by-absolute-path/simple.nix) = $PWD/simple.nix ]]
[[ $(nix-instantiate --find-file by-relative-path/simple.nix) = $PWD/simple.nix ]]
-
-unset NIX_PATH
-
-[[ $(nix-instantiate --option nix-path by-relative-path=. --find-file by-relative-path/simple.nix) = "$PWD/simple.nix" ]]
-[[ $(NIX_PATH= nix-instantiate --option nix-path by-relative-path=. --find-file by-relative-path/simple.nix) = "$PWD/simple.nix" ]]
diff --git a/tests/restricted.sh b/tests/restricted.sh
index 3b6ee2af1..9bd16cf51 100644
--- a/tests/restricted.sh
+++ b/tests/restricted.sh
@@ -17,9 +17,6 @@ nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel'
(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=.
-# no default NIX_PATH
-(unset NIX_PATH; ! nix-instantiate --restrict-eval --find-file .)
-
p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)")
cmp $p restricted.sh