From 62d275d7c0709817c7d7f21dfffbe528f9828bef Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 27 Feb 2026 13:38:45 -0500 Subject: [PATCH 1/5] tests: use pathToUrlPath for file:// URL construction in git test --- src/libfetchers-tests/git.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libfetchers-tests/git.cc b/src/libfetchers-tests/git.cc index abc3dd74c..9fb9a2ce1 100644 --- a/src/libfetchers-tests/git.cc +++ b/src/libfetchers-tests/git.cc @@ -4,6 +4,7 @@ #include "nix/fetchers/fetch-settings.hh" #include "nix/fetchers/fetchers.hh" #include "nix/fetchers/git-utils.hh" +#include "nix/util/url.hh" #include #include @@ -190,7 +191,7 @@ TEST_F(GitTest, submodulePeriodSupport) auto input = fetchers::Input::fromAttrs( settings, { - {"url", "file://" + repoPath.string()}, + {"url", "file://" + encodeUrlPath(pathToUrlPath(repoPath))}, {"submodules", Explicit{true}}, {"type", "git"}, {"ref", "main"}, From b26f2ca3e68bfc670ab3ffddc7e81ab41dd1ea62 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 27 Feb 2026 14:50:00 -0500 Subject: [PATCH 2/5] tests: clear NIX_STORE env in fetchers, expr, and flake test environments --- src/libexpr-tests/meson.build | 2 ++ src/libfetchers-tests/meson.build | 2 ++ src/libflake-tests/meson.build | 1 + 3 files changed, 5 insertions(+) diff --git a/src/libexpr-tests/meson.build b/src/libexpr-tests/meson.build index c5b72851d..0b0a01c20 100644 --- a/src/libexpr-tests/meson.build +++ b/src/libexpr-tests/meson.build @@ -84,6 +84,8 @@ test( this_exe, env : { '_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data', + 'HOME' : meson.current_build_dir() / 'test-home', + 'NIX_STORE' : '', }, protocol : 'gtest', ) diff --git a/src/libfetchers-tests/meson.build b/src/libfetchers-tests/meson.build index 6bccdb05c..ba9774e95 100644 --- a/src/libfetchers-tests/meson.build +++ b/src/libfetchers-tests/meson.build @@ -66,6 +66,8 @@ test( this_exe, env : { '_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data', + 'HOME' : meson.current_build_dir() / 'test-home', + 'NIX_STORE' : '', }, protocol : 'gtest', ) diff --git a/src/libflake-tests/meson.build b/src/libflake-tests/meson.build index 59094abe8..3512be10b 100644 --- a/src/libflake-tests/meson.build +++ b/src/libflake-tests/meson.build @@ -62,6 +62,7 @@ test( '_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data', 'NIX_CONFIG' : 'extra-experimental-features = flakes', 'HOME' : meson.current_build_dir() / 'test-home', + 'NIX_STORE' : '', }, protocol : 'gtest', ) From f3792cdad5c6d8e415b5d125c456acb64c026562 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 27 Feb 2026 14:50:00 -0500 Subject: [PATCH 3/5] flakeref: use portable root detection in directory walk loops --- src/libflake/flakeref.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libflake/flakeref.cc b/src/libflake/flakeref.cc index c35040adb..2960a158e 100644 --- a/src/libflake/flakeref.cc +++ b/src/libflake/flakeref.cc @@ -146,7 +146,7 @@ std::pair parsePathFlakeRefWithFragment( // Save device to detect filesystem boundary dev_t device = lstat(path).st_dev; bool found = false; - while (path != "/") { + while (path.parent_path() != path) { if (pathExists(path / "flake.nix")) { found = true; break; @@ -171,7 +171,7 @@ std::pair parsePathFlakeRefWithFragment( auto flakeRoot = path; std::string subdir; - while (flakeRoot != "/") { + while (flakeRoot.parent_path() != flakeRoot) { if (pathExists(flakeRoot / ".git")) { auto parsedURL = ParsedURL{ .scheme = "git+file", From 9c59f628905ae4c3c6c3d919f1aa15cd66a6e4d2 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 27 Feb 2026 14:50:00 -0500 Subject: [PATCH 4/5] url: handle Windows drive letters in fixGitURL --- src/libutil/url.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 184eba263..abf0acf00 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -408,15 +408,22 @@ ParsedURL fixGitURL(std::string url) url = std::regex_replace(url, scpRegex, "ssh://$1@$2/$3"); if (!hasPrefix(url, "file:") && !hasPrefix(url, "git+file:") && url.find("://") == std::string::npos) { auto path = splitString>(url, "/"); +#ifdef _WIN32 + // Windows drive letters (e.g., "C:") look like SCP hosts but are local paths + bool hasDriveLetter = !path.empty() && path[0].size() == 2 + && std::isalpha(static_cast(path[0][0])) && path[0][1] == ':'; +#else + constexpr bool hasDriveLetter = false; +#endif // Reject SCP-like URLs without user (e.g., "github.com:path") - colon in first component - if (!path.empty() && path[0].find(':') != std::string::npos) + if (!path.empty() && path[0].find(':') != std::string::npos && !hasDriveLetter) throw BadURL("SCP-like URL '%s' is not supported; use SSH URL syntax instead (ssh://...)", url); // Absolute paths get an empty authority (file:///path), relative paths get none (file:path) - if (hasPrefix(url, "/")) + if (hasPrefix(url, "/") || hasDriveLetter) return ParsedURL{ .scheme = "file", .authority = ParsedURL::Authority{}, - .path = path, + .path = hasDriveLetter ? pathToUrlPath(std::filesystem::path(url)) : path, }; else return ParsedURL{ From 0d8ca7a8886c3ff0f8ea2f4eff56e33e2f947d3a Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 27 Feb 2026 14:50:00 -0500 Subject: [PATCH 5/5] fetchers: use pathToUrlPath in PathInputScheme::toURL --- src/libfetchers/path.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index cf7f6aa92..9d846c675 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -95,7 +95,7 @@ struct PathInputScheme : InputScheme query.erase("__final"); return ParsedURL{ .scheme = "path", - .path = splitString>(getStrAttr(input.attrs, "path"), "/"), + .path = pathToUrlPath(std::filesystem::path{getStrAttr(input.attrs, "path")}), .query = query, }; }