Compare commits
64 Commits
string-dat
...
2.32.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7fc293353 | ||
|
|
46a43dede9 | ||
|
|
5b8c24fb31 | ||
|
|
be250f00b9 | ||
|
|
b591265a05 | ||
|
|
70b7c059fd | ||
|
|
287b54b49c | ||
|
|
441aba4823 | ||
|
|
aa657c1679 | ||
|
|
a4fb83a239 | ||
|
|
47ba375285 | ||
|
|
5c9e22d75a | ||
|
|
e6d823e46d | ||
|
|
038cc7913b | ||
|
|
828bf74cd0 | ||
|
|
ec122cbfda | ||
|
|
e1ff27324b | ||
|
|
7b41563055 | ||
|
|
7f9b9c3638 | ||
|
|
ed09f1b4d9 | ||
|
|
fc6811cb51 | ||
|
|
a24df3d4e5 | ||
|
|
e6003b5c4f | ||
|
|
f566957dc4 | ||
|
|
f434a3e3c6 | ||
|
|
939f81c2e6 | ||
|
|
758dacacf4 | ||
|
|
9e4177bc67 | ||
|
|
328a3bbbd0 | ||
|
|
5c9481de19 | ||
|
|
ebadea0734 | ||
|
|
7d7ca3fe96 | ||
|
|
3a92f83e75 | ||
|
|
291e8ab6bd | ||
|
|
19441dd317 | ||
|
|
71ec2cf62d | ||
|
|
b36f8043d2 | ||
|
|
4d1f72a324 | ||
|
|
ac3532d0f2 | ||
|
|
84dbf182d4 | ||
|
|
4a27d70132 | ||
|
|
dadb5b01b7 | ||
|
|
3c39583e55 | ||
|
|
a038c92d38 | ||
|
|
cf6ad228ae | ||
|
|
44701007b4 | ||
|
|
ff1f145992 | ||
|
|
3519ad2ca6 | ||
|
|
16af6a8ed1 | ||
|
|
549a2e8272 | ||
|
|
2531dcad75 | ||
|
|
11f9c59140 | ||
|
|
a25a219e79 | ||
|
|
f07486b205 | ||
|
|
9ec98f7844 | ||
|
|
634e1d3b65 | ||
|
|
70655061e3 | ||
|
|
da328e6004 | ||
|
|
6b16af8c0e | ||
|
|
010b78e0cf | ||
|
|
98b7654390 | ||
|
|
c5799aa62c | ||
|
|
72e3dd396c | ||
|
|
d069633b3d |
@@ -24,9 +24,9 @@ let
|
||||
in
|
||||
concatStringsSep "\n" (map showEntry storesList);
|
||||
|
||||
"index.md" =
|
||||
replaceStrings [ "@store-types@" ] [ index ]
|
||||
(readFile ./source/store/types/index.md.in);
|
||||
"index.md" = replaceStrings [ "@store-types@" ] [ index ] (
|
||||
readFile ./source/store/types/index.md.in
|
||||
);
|
||||
|
||||
tableOfContents =
|
||||
let
|
||||
|
||||
8
flake.lock
generated
8
flake.lock
generated
@@ -63,16 +63,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1756178832,
|
||||
"narHash": "sha256-O2CIn7HjZwEGqBrwu9EU76zlmA5dbmna7jL1XUmAId8=",
|
||||
"lastModified": 1761597516,
|
||||
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d98ce345cdab58477ca61855540999c86577d19d",
|
||||
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.05-small",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05-small";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||
|
||||
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446";
|
||||
@@ -32,7 +32,7 @@
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
linux32BitSystems = [ "i686-linux" ];
|
||||
linux64BitSystems = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# shellcheck disable=all
|
||||
#compdef nix
|
||||
# shellcheck disable=all
|
||||
|
||||
function _nix() {
|
||||
local ifs_bk="$IFS"
|
||||
|
||||
32
nix-meson-build-support/common/assert-fail/meson.build
Normal file
32
nix-meson-build-support/common/assert-fail/meson.build
Normal file
@@ -0,0 +1,32 @@
|
||||
can_wrap_assert_fail_test_code = '''
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
int main()
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
extern "C" void * __real___assert_fail(const char *, const char *, unsigned int, const char *);
|
||||
|
||||
extern "C" void *
|
||||
__wrap___assert_fail(const char *, const char *, unsigned int, const char *)
|
||||
{
|
||||
return __real___assert_fail(nullptr, nullptr, 0, nullptr);
|
||||
}
|
||||
'''
|
||||
|
||||
wrap_assert_fail_args = [ '-Wl,--wrap=__assert_fail' ]
|
||||
|
||||
can_wrap_assert_fail = cxx.links(
|
||||
can_wrap_assert_fail_test_code,
|
||||
args : wrap_assert_fail_args,
|
||||
name : 'linker can wrap __assert_fail',
|
||||
)
|
||||
|
||||
if can_wrap_assert_fail
|
||||
deps_other += declare_dependency(
|
||||
sources : 'wrap-assert-fail.cc',
|
||||
link_args : wrap_assert_fail_args,
|
||||
)
|
||||
endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "nix/util/error.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cinttypes>
|
||||
#include <string_view>
|
||||
|
||||
extern "C" [[noreturn]] void __attribute__((weak))
|
||||
__wrap___assert_fail(const char * assertion, const char * file, unsigned int line, const char * function)
|
||||
{
|
||||
char buf[512];
|
||||
int n =
|
||||
snprintf(buf, sizeof(buf), "Assertion '%s' failed in %s at %s:%" PRIuLEAST32, assertion, function, file, line);
|
||||
if (n < 0)
|
||||
nix::panic("Assertion failed and could not format error message");
|
||||
nix::panic(std::string_view(buf, std::min(static_cast<int>(sizeof(buf)), n)));
|
||||
}
|
||||
@@ -42,5 +42,27 @@ if cxx.get_id() == 'clang'
|
||||
add_project_arguments('-fpch-instantiate-templates', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Darwin ld doesn't like "X.Y.Zpre"
|
||||
nix_soversion = meson.project_version().split('pre')[0]
|
||||
# Detect if we're using libstdc++ (GCC's standard library)
|
||||
# libstdc++ uses Intel TBB as backend for C++17 parallel algorithms when <execution> is included.
|
||||
# boost::concurrent_flat_map includes <execution>, which would require linking against TBB.
|
||||
# Since we don't actually use parallel algorithms, disable the TBB backend to avoid the dependency.
|
||||
# TBB is a dependency of blake3 and leaking into our build environment.
|
||||
is_using_libstdcxx = cxx.compiles(
|
||||
'''
|
||||
#include <ciso646>
|
||||
#ifndef __GLIBCXX__
|
||||
#error "not libstdc++"
|
||||
#endif
|
||||
int main() { return 0; }
|
||||
''',
|
||||
name : 'using libstdc++',
|
||||
)
|
||||
|
||||
if is_using_libstdcxx
|
||||
add_project_arguments('-D_GLIBCXX_USE_TBB_PAR_BACKEND=0', language : 'cpp')
|
||||
endif
|
||||
|
||||
# Darwin ld doesn't like "X.Y.ZpreABCD+W"
|
||||
nix_soversion = meson.project_version().split('+')[0].split('pre')[0]
|
||||
|
||||
subdir('assert-fail')
|
||||
|
||||
@@ -57,15 +57,20 @@ scope: {
|
||||
prevAttrs.postInstall;
|
||||
});
|
||||
|
||||
toml11 = pkgs.toml11.overrideAttrs rec {
|
||||
version = "4.4.0";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ToruNiina";
|
||||
repo = "toml11";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-sgWKYxNT22nw376ttGsTdg0AMzOwp8QH3E8mx0BZJTQ=";
|
||||
};
|
||||
};
|
||||
# TODO: Remove this when https://github.com/NixOS/nixpkgs/pull/442682 is included in a stable release
|
||||
toml11 =
|
||||
if lib.versionAtLeast pkgs.toml11.version "4.4.0" then
|
||||
pkgs.toml11
|
||||
else
|
||||
pkgs.toml11.overrideAttrs rec {
|
||||
version = "4.4.0";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ToruNiina";
|
||||
repo = "toml11";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-sgWKYxNT22nw376ttGsTdg0AMzOwp8QH3E8mx0BZJTQ=";
|
||||
};
|
||||
};
|
||||
|
||||
# TODO Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed.
|
||||
boost =
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "nix/expr/symbol-table.hh"
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/iterator/function_output_iterator.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
@@ -463,12 +464,48 @@ private:
|
||||
return bindings->baseLayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bindings gets "layered" on top of another we need to recalculate
|
||||
* the number of unique attributes in the chain.
|
||||
*
|
||||
* This is done by either iterating over the base "layer" and the newly added
|
||||
* attributes and counting duplicates. If the base "layer" is big this approach
|
||||
* is inefficient and we fall back to doing per-element binary search in the base
|
||||
* "layer".
|
||||
*/
|
||||
void finishSizeIfNecessary()
|
||||
{
|
||||
if (hasBaseLayer())
|
||||
/* NOTE: Do not use std::ranges::distance, since Bindings is a sized
|
||||
range, but we are calculating this size here. */
|
||||
bindings->numAttrsInChain = std::distance(bindings->begin(), bindings->end());
|
||||
if (!hasBaseLayer())
|
||||
return;
|
||||
|
||||
auto & base = *bindings->baseLayer;
|
||||
auto attrs = std::span(bindings->attrs, bindings->numAttrs);
|
||||
|
||||
Bindings::size_type duplicates = 0;
|
||||
|
||||
/* If the base bindings is smaller than the newly added attributes
|
||||
iterate using std::set_intersection to run in O(|base| + |attrs|) =
|
||||
O(|attrs|). Otherwise use an O(|attrs| * log(|base|)) per-attr binary
|
||||
search to check for duplicates. Note that if we are in this code path then
|
||||
|attrs| <= bindingsUpdateLayerRhsSizeThreshold, which 16 by default. We are
|
||||
optimizing for the case when a small attribute set gets "layered" on top of
|
||||
a much larger one. When attrsets are already small it's fine to do a linear
|
||||
scan, but we should avoid expensive iterations over large "base" attrsets. */
|
||||
if (attrs.size() > base.size()) {
|
||||
std::set_intersection(
|
||||
base.begin(),
|
||||
base.end(),
|
||||
attrs.begin(),
|
||||
attrs.end(),
|
||||
boost::make_function_output_iterator([&]([[maybe_unused]] auto && _) { ++duplicates; }));
|
||||
} else {
|
||||
for (const auto & attr : attrs) {
|
||||
if (base.get(attr.name))
|
||||
++duplicates;
|
||||
}
|
||||
}
|
||||
|
||||
bindings->numAttrsInChain = base.numAttrsInChain + attrs.size() - duplicates;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -1973,8 +1973,13 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false);
|
||||
auto dir = dirOf(*path);
|
||||
v.mkString(dir, context);
|
||||
auto pos = path->rfind('/');
|
||||
if (pos == path->npos)
|
||||
v.mkStringMove(".", context);
|
||||
else if (pos == 0)
|
||||
v.mkStringMove("/", context);
|
||||
else
|
||||
v.mkString(path->substr(0, pos), context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,12 @@ TEST_F(GitUtilsTest, peel_reference)
|
||||
|
||||
TEST(GitUtils, isLegalRefName)
|
||||
{
|
||||
ASSERT_TRUE(isLegalRefName("A/b"));
|
||||
ASSERT_TRUE(isLegalRefName("AaA/b"));
|
||||
ASSERT_TRUE(isLegalRefName("FOO/BAR/BAZ"));
|
||||
ASSERT_TRUE(isLegalRefName("HEAD"));
|
||||
ASSERT_TRUE(isLegalRefName("refs/tags/1.2.3"));
|
||||
ASSERT_TRUE(isLegalRefName("refs/heads/master"));
|
||||
ASSERT_TRUE(isLegalRefName("foox"));
|
||||
ASSERT_TRUE(isLegalRefName("1337"));
|
||||
ASSERT_TRUE(isLegalRefName("foo.baz"));
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <git2/attr.h>
|
||||
#include <git2/blob.h>
|
||||
#include <git2/branch.h>
|
||||
#include <git2/commit.h>
|
||||
#include <git2/config.h>
|
||||
#include <git2/describe.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <git2/submodule.h>
|
||||
#include <git2/sys/odb_backend.h>
|
||||
#include <git2/sys/mempack.h>
|
||||
#include <git2/tag.h>
|
||||
#include <git2/tree.h>
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
@@ -1323,63 +1325,33 @@ GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path &
|
||||
return workdirInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the git reference is valid and normalizes slash '/' sequences.
|
||||
*
|
||||
* Accepts shorthand references (one-level refnames are allowed).
|
||||
*/
|
||||
bool isValidRefNameAllowNormalizations(const std::string & refName)
|
||||
{
|
||||
/* Unfortunately libgit2 doesn't expose the limit in headers, but its internal
|
||||
limit is also 1024. */
|
||||
std::array<char, 1024> normalizedRefBuffer;
|
||||
|
||||
/* It would be nice to have a better API like git_reference_name_is_valid, but
|
||||
* with GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND flag. libgit2 uses it internally
|
||||
* but doesn't expose it in public headers [1].
|
||||
* [1]:
|
||||
* https://github.com/libgit2/libgit2/blob/9d5f1bacc23594c2ba324c8f0d41b88bf0e9ef04/src/libgit2/refs.c#L1362-L1365
|
||||
*/
|
||||
|
||||
auto res = git_reference_normalize_name(
|
||||
normalizedRefBuffer.data(),
|
||||
normalizedRefBuffer.size(),
|
||||
refName.c_str(),
|
||||
GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL | GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND);
|
||||
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
bool isLegalRefName(const std::string & refName)
|
||||
{
|
||||
initLibGit2();
|
||||
|
||||
/* Since `git_reference_normalize_name` is the best API libgit2 has for verifying
|
||||
* reference names with shorthands (see comment in normalizeRefName), we need to
|
||||
* ensure that exceptions to the validity checks imposed by normalization [1] are checked
|
||||
* explicitly.
|
||||
* [1]: https://git-scm.com/docs/git-check-ref-format#Documentation/git-check-ref-format.txt---normalize
|
||||
*/
|
||||
|
||||
/* Check for cases that don't get rejected by libgit2.
|
||||
* FIXME: libgit2 should reject this. */
|
||||
if (refName == "@")
|
||||
return false;
|
||||
|
||||
/* Leading slashes and consecutive slashes are stripped during normalizatiton. */
|
||||
if (refName.starts_with('/') || refName.find("//") != refName.npos)
|
||||
return false;
|
||||
|
||||
/* Refer to libgit2. */
|
||||
if (!isValidRefNameAllowNormalizations(refName))
|
||||
return false;
|
||||
|
||||
/* libgit2 doesn't barf on DEL symbol.
|
||||
* FIXME: libgit2 should reject this. */
|
||||
if (refName.find('\177') != refName.npos)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
for (auto * func : {
|
||||
git_reference_name_is_valid,
|
||||
git_branch_name_is_valid,
|
||||
git_tag_name_is_valid,
|
||||
}) {
|
||||
int valid = 0;
|
||||
if (func(&valid, refName.c_str()))
|
||||
throw Error("checking git reference '%s': %s", refName, git_error_last()->message);
|
||||
if (valid)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -164,8 +164,7 @@ struct GitInputScheme : InputScheme
|
||||
{
|
||||
std::optional<Input> inputFromURL(const Settings & settings, const ParsedURL & url, bool requireTree) const override
|
||||
{
|
||||
auto parsedScheme = parseUrlScheme(url.scheme);
|
||||
if (parsedScheme.application != "git")
|
||||
if (url.scheme != "git" && parseUrlScheme(url.scheme).application != "git")
|
||||
return {};
|
||||
|
||||
auto url2(url);
|
||||
@@ -496,6 +495,36 @@ struct GitInputScheme : InputScheme
|
||||
Git interprets them as part of the file name. So get
|
||||
rid of them. */
|
||||
url.query.clear();
|
||||
/* Backward compatibility hack: In old versions of Nix, if you had
|
||||
a flake input like
|
||||
|
||||
inputs.foo.url = "git+https://foo/bar?dir=subdir";
|
||||
|
||||
it would result in a lock file entry like
|
||||
|
||||
"original": {
|
||||
"dir": "subdir",
|
||||
"type": "git",
|
||||
"url": "https://foo/bar?dir=subdir"
|
||||
}
|
||||
|
||||
New versions of Nix remove `?dir=subdir` from the `url` field,
|
||||
since the subdirectory is intended for `FlakeRef`, not the
|
||||
fetcher (and specifically the remote server), that is, the
|
||||
flakeref is parsed into
|
||||
|
||||
"original": {
|
||||
"dir": "subdir",
|
||||
"type": "git",
|
||||
"url": "https://foo/bar"
|
||||
}
|
||||
|
||||
However, new versions of nix parsing old flake.lock files would pass the dir=
|
||||
query parameter in the "url" attribute to git, which will then complain.
|
||||
|
||||
For this reason, we are filtering the `dir` query parameter from the URL
|
||||
before passing it to git. */
|
||||
url.query.erase("dir");
|
||||
repoInfo.location = url;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,9 +158,12 @@ struct Setter
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks that the git reference is valid and normalized.
|
||||
* Checks that the string can be a valid git reference, branch or tag name.
|
||||
* Accepts shorthand references (one-level refnames are allowed), pseudorefs
|
||||
* like `HEAD`.
|
||||
*
|
||||
* Accepts shorthand references (one-level refnames are allowed).
|
||||
* @note This is a coarse test to make sure that the refname is at least something
|
||||
* that Git can make sense of.
|
||||
*/
|
||||
bool isLegalRefName(const std::string & refName);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ DownloadFileResult downloadFile(
|
||||
if (cached && !cached->expired)
|
||||
return useCached();
|
||||
|
||||
FileTransferRequest request(ValidURL{url});
|
||||
FileTransferRequest request(VerbatimURL{url});
|
||||
request.headers = headers;
|
||||
if (cached)
|
||||
request.expectedETag = getStrAttr(cached->value, "etag");
|
||||
@@ -107,13 +107,13 @@ DownloadFileResult downloadFile(
|
||||
static DownloadTarballResult downloadTarball_(
|
||||
const Settings & settings, const std::string & urlS, const Headers & headers, const std::string & displayPrefix)
|
||||
{
|
||||
ValidURL url = urlS;
|
||||
ParsedURL url = parseURL(urlS);
|
||||
|
||||
// Some friendly error messages for common mistakes.
|
||||
// Namely lets catch when the url is a local file path, but
|
||||
// it is not in fact a tarball.
|
||||
if (url.scheme() == "file") {
|
||||
std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path());
|
||||
if (url.scheme == "file") {
|
||||
std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path);
|
||||
if (!exists(localPath)) {
|
||||
throw Error("tarball '%s' does not exist.", localPath);
|
||||
}
|
||||
@@ -164,7 +164,7 @@ static DownloadTarballResult downloadTarball_(
|
||||
|
||||
/* Note: if the download is cached, `importTarball()` will receive
|
||||
no data, which causes it to import an empty tarball. */
|
||||
auto archive = !url.path().empty() && hasSuffix(toLower(url.path().back()), ".zip") ? ({
|
||||
auto archive = !url.path.empty() && hasSuffix(toLower(url.path.back()), ".zip") ? ({
|
||||
/* In streaming mode, libarchive doesn't handle
|
||||
symlinks in zip files correctly (#10649). So write
|
||||
the entire file to disk so libarchive can access it
|
||||
@@ -178,7 +178,7 @@ static DownloadTarballResult downloadTarball_(
|
||||
}
|
||||
TarArchive{path};
|
||||
})
|
||||
: TarArchive{*source};
|
||||
: TarArchive{*source};
|
||||
auto tarballCache = getTarballCache();
|
||||
auto parseSink = tarballCache->getFileSystemObjectSink();
|
||||
auto lastModified = unpackTarfileToSink(archive, *parseSink);
|
||||
|
||||
@@ -199,6 +199,28 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
.description = "flake_id_ref_branch_ignore_empty_segments_ref_rev",
|
||||
.expectedUrl = "flake:nixpkgs/branch/2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
|
||||
},
|
||||
InputFromURLTestCase{
|
||||
.url = "git://somewhere/repo?ref=branch",
|
||||
.attrs =
|
||||
{
|
||||
{"type", Attr("git")},
|
||||
{"ref", Attr("branch")},
|
||||
{"url", Attr("git://somewhere/repo")},
|
||||
},
|
||||
.description = "plain_git_with_ref",
|
||||
.expectedUrl = "git://somewhere/repo?ref=branch",
|
||||
},
|
||||
InputFromURLTestCase{
|
||||
.url = "git+https://somewhere.aaaaaaa/repo?ref=branch",
|
||||
.attrs =
|
||||
{
|
||||
{"type", Attr("git")},
|
||||
{"ref", Attr("branch")},
|
||||
{"url", Attr("https://somewhere.aaaaaaa/repo")},
|
||||
},
|
||||
.description = "git_https_with_ref",
|
||||
.expectedUrl = "git+https://somewhere.aaaaaaa/repo?ref=branch",
|
||||
},
|
||||
InputFromURLTestCase{
|
||||
// Note that this is different from above because the "flake id" shorthand
|
||||
// doesn't allow this.
|
||||
|
||||
@@ -80,7 +80,8 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
|
||||
std::smatch match;
|
||||
auto succeeds = std::regex_match(url, match, pathFlakeRegex);
|
||||
assert(succeeds);
|
||||
if (!succeeds)
|
||||
throw Error("invalid flakeref '%s'", url);
|
||||
auto path = match[1].str();
|
||||
auto query = decodeQuery(match[3].str(), /*lenient=*/true);
|
||||
auto fragment = percentDecode(match[5].str());
|
||||
|
||||
@@ -320,34 +320,29 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
|
||||
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
fun();
|
||||
} catch (...) {
|
||||
/* Subtle: we have to make sure that any `interrupted'
|
||||
condition is discharged before we reach printMsg()
|
||||
below, since otherwise it will throw an (uncaught)
|
||||
exception. */
|
||||
setInterruptThrown();
|
||||
throw;
|
||||
}
|
||||
} catch (Exit & e) {
|
||||
return e.status;
|
||||
} catch (UsageError & e) {
|
||||
logError(e.info());
|
||||
printError("Try '%1% --help' for more information.", programName);
|
||||
return 1;
|
||||
} catch (BaseError & e) {
|
||||
logError(e.info());
|
||||
return e.info().status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
printError(error + "out of memory");
|
||||
return 1;
|
||||
} catch (std::exception & e) {
|
||||
printError(error + e.what());
|
||||
return 1;
|
||||
fun();
|
||||
} catch (...) {
|
||||
/* Subtle: we have to make sure that any `interrupted'
|
||||
condition is discharged before we reach printMsg()
|
||||
below, since otherwise it will throw an (uncaught)
|
||||
exception. */
|
||||
setInterruptThrown();
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
/* In case logger also throws just give up. */
|
||||
} catch (Exit & e) {
|
||||
return e.status;
|
||||
} catch (UsageError & e) {
|
||||
logError(e.info());
|
||||
printError("Try '%1% --help' for more information.", programName);
|
||||
return 1;
|
||||
} catch (BaseError & e) {
|
||||
logError(e.info());
|
||||
return e.info().status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
printError(error + "out of memory");
|
||||
return 1;
|
||||
} catch (std::exception & e) {
|
||||
printError(error + e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0]?a=b&c=d
|
||||
@@ -0,0 +1 @@
|
||||
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]?a=b&c=d
|
||||
@@ -0,0 +1 @@
|
||||
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25?a=b&c=d
|
||||
@@ -0,0 +1 @@
|
||||
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d
|
||||
@@ -0,0 +1 @@
|
||||
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d
|
||||
@@ -0,0 +1 @@
|
||||
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0
|
||||
@@ -183,4 +183,64 @@ static StoreReference sshIPv6AuthorityWithUserinfoAndParams{
|
||||
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_3, sshIPv6AuthorityWithUserinfoAndParams)
|
||||
|
||||
static const StoreReference sshIPv6AuthorityWithUserinfoAndParamsAndZoneId{
|
||||
.variant =
|
||||
StoreReference::Specified{
|
||||
.scheme = "ssh",
|
||||
.authority = "userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]",
|
||||
},
|
||||
.params =
|
||||
{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
};
|
||||
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_4, sshIPv6AuthorityWithUserinfoAndParamsAndZoneId)
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_5, sshIPv6AuthorityWithUserinfoAndParamsAndZoneId)
|
||||
|
||||
static const StoreReference sshIPv6AuthorityWithUserinfoAndParamsAndZoneIdTricky{
|
||||
.variant =
|
||||
StoreReference::Specified{
|
||||
.scheme = "ssh",
|
||||
.authority = "userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%2525]",
|
||||
},
|
||||
.params =
|
||||
{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
};
|
||||
|
||||
// Non-standard syntax where the IPv6 literal appears without brackets. In
|
||||
// this case don't considering %25 to be a pct-encoded % and just take it as a
|
||||
// literal value. 25 is a perfectly legal ZoneId value in theory.
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_6, sshIPv6AuthorityWithUserinfoAndParamsAndZoneIdTricky)
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_7, sshIPv6AuthorityWithUserinfoAndParamsAndZoneId)
|
||||
|
||||
static const StoreReference sshIPv6AuthorityWithParamsAndZoneId{
|
||||
.variant =
|
||||
StoreReference::Specified{
|
||||
.scheme = "ssh",
|
||||
.authority = "[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]",
|
||||
},
|
||||
.params =
|
||||
{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
};
|
||||
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_8, sshIPv6AuthorityWithParamsAndZoneId)
|
||||
|
||||
static const StoreReference sshIPv6AuthorityWithZoneId{
|
||||
.variant =
|
||||
StoreReference::Specified{
|
||||
.scheme = "ssh",
|
||||
.authority = "[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]",
|
||||
},
|
||||
};
|
||||
|
||||
URI_TEST_READ(ssh_unbracketed_ipv6_9, sshIPv6AuthorityWithZoneId)
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -860,7 +860,15 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||
{
|
||||
builder.reset();
|
||||
StorePathSet outputPaths;
|
||||
for (auto & [_, output] : builtOutputs) {
|
||||
/* In the check case we install no store objects, and so
|
||||
`builtOutputs` is empty. However, per issue #14287, there is
|
||||
an expectation that the post-build hook is still executed.
|
||||
(This is useful for e.g. logging successful deterministic rebuilds.)
|
||||
|
||||
In order to make that work, in the check case just load the
|
||||
(preexisting) infos from scratch, rather than relying on what
|
||||
`DerivationBuilder` returned to us. */
|
||||
for (auto & [_, output] : buildMode == bmCheck ? checkPathValidity(initialOutputs).second : builtOutputs) {
|
||||
// for sake of `bmRepair`
|
||||
worker.markContentsGood(output.outPath);
|
||||
outputPaths.insert(output.outPath);
|
||||
|
||||
@@ -182,7 +182,19 @@ Goal::Co DerivationGoal::haveDerivation()
|
||||
}
|
||||
}
|
||||
|
||||
assert(success.builtOutputs.count(wantedOutput) > 0);
|
||||
/* If the wanted output is not in builtOutputs (e.g., because it
|
||||
was already valid and therefore not re-registered), we need to
|
||||
add it ourselves to ensure we return the correct information. */
|
||||
if (success.builtOutputs.count(wantedOutput) == 0) {
|
||||
debug(
|
||||
"BUG! wanted output '%s' not in builtOutputs, working around by adding it manually", wantedOutput);
|
||||
auto realisation = assertPathValidity();
|
||||
realisation.id = DrvOutput{
|
||||
.drvHash = outputHash,
|
||||
.outputName = wantedOutput,
|
||||
};
|
||||
success.builtOutputs.emplace(wantedOutput, std::move(realisation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ static void builtinFetchurl(const BuiltinBuilderContext & ctx)
|
||||
|
||||
auto fetch = [&](const std::string & url) {
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
FileTransferRequest request(ValidURL{url});
|
||||
FileTransferRequest request(VerbatimURL{url});
|
||||
request.decompress = false;
|
||||
|
||||
auto decompressor = makeDecompressionSink(unpack && hasSuffix(mainUrl, ".xz") ? "xz" : "none", sink);
|
||||
|
||||
@@ -99,6 +99,17 @@ DerivationOptions DerivationOptions::fromStructuredAttrs(
|
||||
return fromStructuredAttrs(env, parsed ? &*parsed : nullptr);
|
||||
}
|
||||
|
||||
static void flatten(const nlohmann::json & value, StringSet & res)
|
||||
{
|
||||
if (value.is_array())
|
||||
for (auto & v : value)
|
||||
flatten(v, res);
|
||||
else if (value.is_string())
|
||||
res.insert(value);
|
||||
else
|
||||
throw Error("'exportReferencesGraph' value is not an array or a string");
|
||||
}
|
||||
|
||||
DerivationOptions
|
||||
DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn)
|
||||
{
|
||||
@@ -219,12 +230,9 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
|
||||
if (!e || !e->is_object())
|
||||
return ret;
|
||||
for (auto & [key, value] : getObject(*e)) {
|
||||
if (value.is_array())
|
||||
ret.insert_or_assign(key, value);
|
||||
else if (value.is_string())
|
||||
ret.insert_or_assign(key, StringSet{value});
|
||||
else
|
||||
throw Error("'exportReferencesGraph' value is not an array or a string");
|
||||
StringSet ss;
|
||||
flatten(value, ss);
|
||||
ret.insert_or_assign(key, std::move(ss));
|
||||
}
|
||||
} else {
|
||||
auto s = getOr(env, "exportReferencesGraph", "");
|
||||
|
||||
@@ -79,7 +79,7 @@ extern const unsigned int RETRY_TIME_MS_DEFAULT;
|
||||
|
||||
struct FileTransferRequest
|
||||
{
|
||||
ValidURL uri;
|
||||
VerbatimURL uri;
|
||||
Headers headers;
|
||||
std::string expectedETag;
|
||||
bool verifyTLS = true;
|
||||
@@ -93,7 +93,7 @@ struct FileTransferRequest
|
||||
std::string mimeType;
|
||||
std::function<void(std::string_view data)> dataCallback;
|
||||
|
||||
FileTransferRequest(ValidURL uri)
|
||||
FileTransferRequest(VerbatimURL uri)
|
||||
: uri(std::move(uri))
|
||||
, parentAct(getCurActivity())
|
||||
{
|
||||
|
||||
@@ -52,7 +52,21 @@ struct RestrictionContext
|
||||
* Add 'path' to the set of paths that may be referenced by the
|
||||
* outputs, and make it appear in the sandbox.
|
||||
*/
|
||||
virtual void addDependency(const StorePath & path) = 0;
|
||||
void addDependency(const StorePath & path)
|
||||
{
|
||||
if (isAllowed(path))
|
||||
return;
|
||||
addDependencyImpl(path);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* This is the underlying implementation to be defined. The caller
|
||||
* will ensure that this is only called on newly added dependencies,
|
||||
* and that idempotent calls are a no-op.
|
||||
*/
|
||||
virtual void addDependencyImpl(const StorePath & path) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,6 +82,8 @@ struct ServeProto::BasicClientConnection
|
||||
BuildResult getBuildDerivationResponse(const StoreDirConfig & store);
|
||||
|
||||
void narFromPath(const StoreDirConfig & store, const StorePath & path, std::function<void(Source &)> fun);
|
||||
|
||||
void importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun);
|
||||
};
|
||||
|
||||
struct ServeProto::BasicServerConnection
|
||||
|
||||
@@ -108,6 +108,13 @@ enum struct ServeProto::Command : uint64_t {
|
||||
QueryValidPaths = 1,
|
||||
QueryPathInfos = 2,
|
||||
DumpStorePath = 3,
|
||||
/**
|
||||
* @note This is no longer used by Nix (as a client), but it is used
|
||||
* by Hydra. We should therefore not remove it until Hydra no longer
|
||||
* uses it either.
|
||||
*/
|
||||
ImportPaths = 4,
|
||||
// ExportPaths = 5,
|
||||
BuildPaths = 6,
|
||||
QueryClosure = 7,
|
||||
BuildDerivation = 8,
|
||||
|
||||
@@ -1383,7 +1383,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
for (auto & link : DirectoryIterator{linksDir}) {
|
||||
checkInterrupt();
|
||||
auto name = link.path().filename();
|
||||
printMsg(lvlTalkative, "checking contents of '%s'", name);
|
||||
printMsg(lvlTalkative, "checking contents of %s", name);
|
||||
PosixSourceAccessor accessor;
|
||||
std::string hash = hashPath(
|
||||
PosixSourceAccessor::createAtRoot(link.path()),
|
||||
@@ -1391,10 +1391,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
HashAlgorithm::SHA256)
|
||||
.first.to_string(HashFormat::Nix32, false);
|
||||
if (hash != name.string()) {
|
||||
printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash);
|
||||
printError("link %s was modified! expected hash %s, got '%s'", link.path(), name, hash);
|
||||
if (repair) {
|
||||
std::filesystem::remove(link.path());
|
||||
printInfo("removed link '%s'", link.path());
|
||||
printInfo("removed link %s", link.path());
|
||||
} else {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ void LocalStore::optimisePath_(
|
||||
full. When that happens, it's fine to ignore it: we
|
||||
just effectively disable deduplication of this
|
||||
file. */
|
||||
printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno));
|
||||
printInfo("cannot link %s to '%s': %s", linkPath, path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,11 +216,11 @@ void LocalStore::optimisePath_(
|
||||
auto stLink = lstat(linkPath.string());
|
||||
|
||||
if (st.st_ino == stLink.st_ino) {
|
||||
debug("'%1%' is already linked to '%2%'", path, linkPath);
|
||||
debug("'%1%' is already linked to %2%", path, linkPath);
|
||||
return;
|
||||
}
|
||||
|
||||
printMsg(lvlTalkative, "linking '%1%' to '%2%'", path, linkPath);
|
||||
printMsg(lvlTalkative, "linking '%1%' to %2%", path, linkPath);
|
||||
|
||||
/* Make the containing directory writable, but only if it's not
|
||||
the store itself (we don't want or need to mess with its
|
||||
@@ -245,7 +245,7 @@ void LocalStore::optimisePath_(
|
||||
systems). This is likely to happen with empty files.
|
||||
Just shrug and ignore. */
|
||||
if (st.st_size)
|
||||
printInfo("'%1%' has maximum number of links", linkPath);
|
||||
printInfo("%1% has maximum number of links", linkPath);
|
||||
return;
|
||||
}
|
||||
throw;
|
||||
@@ -256,13 +256,13 @@ void LocalStore::optimisePath_(
|
||||
std::filesystem::rename(tempLink, path);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
std::filesystem::remove(tempLink);
|
||||
printError("unable to unlink '%1%'", tempLink);
|
||||
printError("unable to unlink %1%", tempLink);
|
||||
if (e.code() == std::errc::too_many_links) {
|
||||
/* Some filesystems generate too many links on the rename,
|
||||
rather than on the original link. (Probably it
|
||||
temporarily increases the st_nlink field before
|
||||
decreasing it again.) */
|
||||
debug("'%s' has reached maximum number of links", linkPath);
|
||||
debug("%s has reached maximum number of links", linkPath);
|
||||
return;
|
||||
}
|
||||
throw;
|
||||
|
||||
@@ -93,4 +93,14 @@ void ServeProto::BasicClientConnection::narFromPath(
|
||||
fun(from);
|
||||
}
|
||||
|
||||
void ServeProto::BasicClientConnection::importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun)
|
||||
{
|
||||
to << ServeProto::Command::ImportPaths;
|
||||
fun(to);
|
||||
to.flush();
|
||||
|
||||
if (readInt(from) != 1)
|
||||
throw Error("remote machine failed to import closure");
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -121,7 +121,27 @@ StoreReference StoreReference::parse(const std::string & uri, const StoreReferen
|
||||
* greedily assumed to be the part of the host address. */
|
||||
auto authorityString = schemeAndAuthority->authority;
|
||||
auto userinfo = splitPrefixTo(authorityString, '@');
|
||||
auto maybeIpv6 = boost::urls::parse_ipv6_address(authorityString);
|
||||
/* Back-compat shim for ZoneId specifiers. Technically this isn't
|
||||
* standard, but the expectation is this works with the old syntax
|
||||
* for ZoneID specifiers. For the full story behind the fiasco that
|
||||
* is ZoneID in URLs look at [^].
|
||||
* [^]: https://datatracker.ietf.org/doc/html/draft-schinazi-httpbis-link-local-uri-bcp-03
|
||||
*/
|
||||
|
||||
/* Fish out the internals from inside square brackets. It might be that the pct-sign is unencoded and that's
|
||||
* why we failed to parse it previously. */
|
||||
if (authorityString.starts_with('[') && authorityString.ends_with(']')) {
|
||||
authorityString.remove_prefix(1);
|
||||
authorityString.remove_suffix(1);
|
||||
}
|
||||
|
||||
auto maybeBeforePct = splitPrefixTo(authorityString, '%');
|
||||
bool hasZoneId = maybeBeforePct.has_value();
|
||||
auto maybeZoneId = hasZoneId ? std::optional{authorityString} : std::nullopt;
|
||||
|
||||
std::string_view maybeIpv6S = maybeBeforePct.value_or(authorityString);
|
||||
auto maybeIpv6 = boost::urls::parse_ipv6_address(maybeIpv6S);
|
||||
|
||||
if (maybeIpv6) {
|
||||
std::string fixedAuthority;
|
||||
if (userinfo) {
|
||||
@@ -129,7 +149,11 @@ StoreReference StoreReference::parse(const std::string & uri, const StoreReferen
|
||||
fixedAuthority += '@';
|
||||
}
|
||||
fixedAuthority += '[';
|
||||
fixedAuthority += authorityString;
|
||||
fixedAuthority += maybeIpv6S;
|
||||
if (maybeZoneId) {
|
||||
fixedAuthority += "%25"; // pct-encoded percent character
|
||||
fixedAuthority += *maybeZoneId;
|
||||
}
|
||||
fixedAuthority += ']';
|
||||
return {
|
||||
.variant =
|
||||
|
||||
@@ -325,7 +325,7 @@ private:
|
||||
|
||||
protected:
|
||||
|
||||
void addDependency(const StorePath & path) override;
|
||||
void addDependencyImpl(const StorePath & path) override;
|
||||
|
||||
/**
|
||||
* Make a file owned by the builder.
|
||||
@@ -1181,11 +1181,8 @@ void DerivationBuilderImpl::stopDaemon()
|
||||
daemonSocket.close();
|
||||
}
|
||||
|
||||
void DerivationBuilderImpl::addDependency(const StorePath & path)
|
||||
void DerivationBuilderImpl::addDependencyImpl(const StorePath & path)
|
||||
{
|
||||
if (isAllowed(path))
|
||||
return;
|
||||
|
||||
addedPaths.insert(path);
|
||||
}
|
||||
|
||||
@@ -1717,7 +1714,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||
if (buildMode == bmRepair) {
|
||||
/* Path already exists, need to replace it */
|
||||
replaceValidPath(store.toRealPath(finalDestPath), actualPath);
|
||||
actualPath = store.toRealPath(finalDestPath);
|
||||
} else if (buildMode == bmCheck) {
|
||||
/* Path already exists, and we want to compare, so we leave out
|
||||
new path in place. */
|
||||
@@ -1731,7 +1727,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||
auto destPath = store.toRealPath(finalDestPath);
|
||||
deletePath(destPath);
|
||||
movePath(actualPath, destPath);
|
||||
actualPath = destPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1784,7 +1779,9 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||
debug("unreferenced input: '%1%'", store.printStorePath(i));
|
||||
}
|
||||
|
||||
store.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences()
|
||||
if (!store.isValidPath(newInfo.path))
|
||||
store.optimisePath(
|
||||
store.toRealPath(finalDestPath), NoRepair); // FIXME: combine with scanForReferences()
|
||||
|
||||
newInfo.deriver = drvPath;
|
||||
newInfo.ultimate = true;
|
||||
|
||||
@@ -703,8 +703,11 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu
|
||||
DerivationBuilderImpl::killSandbox(getStats);
|
||||
}
|
||||
|
||||
void addDependency(const StorePath & path) override
|
||||
void addDependencyImpl(const StorePath & path) override
|
||||
{
|
||||
if (isAllowed(path))
|
||||
return;
|
||||
|
||||
auto [source, target] = ChrootDerivationBuilder::addDependencyPrep(path);
|
||||
|
||||
/* Bind-mount the path into the sandbox. This requires
|
||||
|
||||
@@ -868,6 +868,12 @@ TEST_P(ParsedURLPathSegmentsTest, segmentsAreCorrect)
|
||||
EXPECT_EQ(encodeUrlPath(segments), testCase.path);
|
||||
}
|
||||
|
||||
TEST_P(ParsedURLPathSegmentsTest, to_string)
|
||||
{
|
||||
const auto & testCase = GetParam();
|
||||
EXPECT_EQ(testCase.url, parseURL(testCase.url).to_string());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ParsedURL,
|
||||
ParsedURLPathSegmentsTest,
|
||||
@@ -886,6 +892,13 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
.skipEmpty = false,
|
||||
.description = "empty_authority_empty_path",
|
||||
},
|
||||
ParsedURLPathSegmentsTestCase{
|
||||
.url = "path:/",
|
||||
.segments = {"", ""},
|
||||
.path = "/",
|
||||
.skipEmpty = false,
|
||||
.description = "empty_authority_root_path",
|
||||
},
|
||||
ParsedURLPathSegmentsTestCase{
|
||||
.url = "scheme:///",
|
||||
.segments = {"", ""},
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "nix/util/error.hh"
|
||||
#include "nix/util/canon-path.hh"
|
||||
#include "nix/util/split.hh"
|
||||
#include "nix/util/util.hh"
|
||||
#include "nix/util/variant-wrapper.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -342,8 +345,7 @@ ParsedURL fixGitURL(const std::string & url);
|
||||
bool isValidSchemeName(std::string_view scheme);
|
||||
|
||||
/**
|
||||
* Either a ParsedURL or a verbatim string, but the string must be a valid
|
||||
* ParsedURL. This is necessary because in certain cases URI must be passed
|
||||
* Either a ParsedURL or a verbatim string. This is necessary because in certain cases URI must be passed
|
||||
* verbatim (e.g. in builtin fetchers), since those are specified by the user.
|
||||
* In those cases normalizations performed by the ParsedURL might be surprising
|
||||
* and undesirable, since Nix must be a universal client that has to work with
|
||||
@@ -354,23 +356,23 @@ bool isValidSchemeName(std::string_view scheme);
|
||||
*
|
||||
* Though we perform parsing and validation for internal needs.
|
||||
*/
|
||||
struct ValidURL : private ParsedURL
|
||||
struct VerbatimURL
|
||||
{
|
||||
std::optional<std::string> encoded;
|
||||
using Raw = std::variant<std::string, ParsedURL>;
|
||||
Raw raw;
|
||||
|
||||
ValidURL(std::string str)
|
||||
: ParsedURL(parseURL(str, /*lenient=*/false))
|
||||
, encoded(std::move(str))
|
||||
VerbatimURL(std::string_view s)
|
||||
: raw(std::string{s})
|
||||
{
|
||||
}
|
||||
|
||||
ValidURL(std::string_view str)
|
||||
: ValidURL(std::string{str})
|
||||
VerbatimURL(std::string s)
|
||||
: raw(std::move(s))
|
||||
{
|
||||
}
|
||||
|
||||
ValidURL(ParsedURL parsed)
|
||||
: ParsedURL{std::move(parsed)}
|
||||
VerbatimURL(ParsedURL url)
|
||||
: raw(std::move(url))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -379,25 +381,35 @@ struct ValidURL : private ParsedURL
|
||||
*/
|
||||
std::string to_string() const
|
||||
{
|
||||
return encoded.or_else([&]() -> std::optional<std::string> { return ParsedURL::to_string(); }).value();
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](const std::string & str) { return str; }, [](const ParsedURL & url) { return url.to_string(); }},
|
||||
raw);
|
||||
}
|
||||
|
||||
const ParsedURL & parsed() const &
|
||||
const ParsedURL parsed() const
|
||||
{
|
||||
return *this;
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](const std::string & str) { return parseURL(str); }, [](const ParsedURL & url) { return url; }},
|
||||
raw);
|
||||
}
|
||||
|
||||
std::string_view scheme() const &
|
||||
{
|
||||
return ParsedURL::scheme;
|
||||
}
|
||||
|
||||
const auto & path() const &
|
||||
{
|
||||
return ParsedURL::path;
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](std::string_view str) {
|
||||
auto scheme = splitPrefixTo(str, ':');
|
||||
if (!scheme)
|
||||
throw BadURL("URL '%s' doesn't have a scheme", str);
|
||||
return *scheme;
|
||||
},
|
||||
[](const ParsedURL & url) -> std::string_view { return url.scheme; }},
|
||||
raw);
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const ValidURL & url);
|
||||
std::ostream & operator<<(std::ostream & os, const VerbatimURL & url);
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -64,7 +64,7 @@ boost = dependency(
|
||||
'url',
|
||||
],
|
||||
include_type : 'system',
|
||||
version : '>=1.82.0',
|
||||
version : '>=1.87.0',
|
||||
)
|
||||
# boost is a public dependency, but not a pkg-config dependency unfortunately, so we
|
||||
# put in `deps_other`.
|
||||
|
||||
@@ -350,7 +350,7 @@ std::string ParsedURL::renderAuthorityAndPath() const
|
||||
must either be empty or begin with a slash ("/") character. */
|
||||
assert(path.empty() || path.front().empty());
|
||||
res += authority->to_string();
|
||||
} else if (std::ranges::equal(std::views::take(path, 2), std::views::repeat("", 2))) {
|
||||
} else if (std::ranges::equal(std::views::take(path, 3), std::views::repeat("", 3))) {
|
||||
/* If a URI does not contain an authority component, then the path cannot begin
|
||||
with two slash characters ("//") */
|
||||
unreachable();
|
||||
@@ -434,7 +434,7 @@ bool isValidSchemeName(std::string_view s)
|
||||
return std::regex_match(s.begin(), s.end(), regex, std::regex_constants::match_default);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const ValidURL & url)
|
||||
std::ostream & operator<<(std::ostream & os, const VerbatimURL & url)
|
||||
{
|
||||
os << url.to_string();
|
||||
return os;
|
||||
|
||||
@@ -798,8 +798,6 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
// via substitution, as `nix flake check` only needs to verify buildability,
|
||||
// not actually produce the outputs.
|
||||
auto missing = store->queryMissing(drvPaths);
|
||||
// Only occurs if `drvPaths` contains a `DerivedPath::Opaque`, which should never happen
|
||||
assert(missing.unknown.empty());
|
||||
|
||||
std::vector<DerivedPath> toBuild;
|
||||
for (auto & path : missing.willBuild) {
|
||||
|
||||
@@ -986,6 +986,16 @@ static void opServe(Strings opFlags, Strings opArgs)
|
||||
store->narFromPath(store->parseStorePath(readString(in)), out);
|
||||
break;
|
||||
|
||||
case ServeProto::Command::ImportPaths: {
|
||||
if (!writeAllowed)
|
||||
throw Error("importing paths is not allowed");
|
||||
// FIXME: should we skip sig checking?
|
||||
importPaths(*store, in, NoCheckSigs);
|
||||
// indicate success
|
||||
out << 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ServeProto::Command::BuildPaths: {
|
||||
|
||||
if (!writeAllowed)
|
||||
|
||||
@@ -105,7 +105,7 @@ std::tuple<StorePath, Hash> prefetchFile(
|
||||
|
||||
FdSink sink(fd.get());
|
||||
|
||||
FileTransferRequest req(ValidURL{url});
|
||||
FileTransferRequest req(VerbatimURL{url});
|
||||
req.decompress = false;
|
||||
getFileTransfer()->download(std::move(req), sink);
|
||||
}
|
||||
|
||||
12
tests/functional/build-hook-list-paths.sh
Executable file
12
tests/functional/build-hook-list-paths.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
[ -n "$OUT_PATHS" ]
|
||||
[ -n "$DRV_PATH" ]
|
||||
[ -n "$HOOK_DEST" ]
|
||||
|
||||
for o in $OUT_PATHS; do
|
||||
echo "$o" >> "$HOOK_DEST"
|
||||
done
|
||||
@@ -59,6 +59,9 @@ invalid_ref() {
|
||||
}
|
||||
|
||||
|
||||
valid_ref 'A/b'
|
||||
valid_ref 'AaA/b'
|
||||
valid_ref 'FOO/BAR/BAZ'
|
||||
valid_ref 'foox'
|
||||
valid_ref '1337'
|
||||
valid_ref 'foo.baz'
|
||||
|
||||
@@ -88,8 +88,3 @@ requireDaemonNewerThan "2.20"
|
||||
expected=100
|
||||
if [[ -v NIX_DAEMON_PACKAGE ]]; then expected=1; fi # work around the daemon not returning a 100 status correctly
|
||||
expectStderr $expected nix-build --expr '{ url }: builtins.derivation { name = "nix-cache-info"; system = "x86_64-linux"; builder = "builtin:fetchurl"; inherit url; outputHashMode = "flat"; }' --argstr url "file://$narxz" 2>&1 | grep 'must be a fixed-output or impure derivation'
|
||||
|
||||
requireDaemonNewerThan "2.32.0pre20250831"
|
||||
|
||||
expect 1 nix-build --expr 'import <nix/fetchurl.nix>' --argstr name 'name' --argstr url "file://authority.not.allowed/fetchurl.sh?a=1&a=2" --no-out-link |&
|
||||
grepQuiet "error: file:// URL 'file://authority.not.allowed/fetchurl.sh?a=1&a=2' has unexpected authority 'authority.not.allowed'"
|
||||
|
||||
1
tests/functional/lang/eval-okay-builtins-dirOf.exp
Normal file
1
tests/functional/lang/eval-okay-builtins-dirOf.exp
Normal file
@@ -0,0 +1 @@
|
||||
{ pathDoesntExistNested1 = /totallydoesntexistreally; pathDoesntExistNested2 = /totallydoesntexistreally/subdir1; pathDoesntExistRoot = /; pathRoot = /; stringEmpty = "."; stringMultipleSeps = "a//"; stringNoSep = "."; stringRoot = "/"; stringRootA = "/"; stringRootSlash = "/"; stringRootSlashSlash = "//"; stringSingleDir = "a"; stringWithDot = "a/b/c/."; stringWithDotAndDotDot = "a/b/c/../."; stringWithDotAndDotDotSep2 = "a/b/c/.././"; stringWithDotDot = "a/b/c/.."; stringWithDotDotSep2 = "a/b/c/../"; stringWithDotSep2 = "a/b/c/./"; }
|
||||
21
tests/functional/lang/eval-okay-builtins-dirOf.nix
Normal file
21
tests/functional/lang/eval-okay-builtins-dirOf.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
stringEmpty = dirOf "";
|
||||
stringNoSep = dirOf "filename";
|
||||
stringSingleDir = dirOf "a/b";
|
||||
stringMultipleSeps = dirOf "a///b";
|
||||
stringRoot = dirOf "/";
|
||||
stringRootSlash = dirOf "//";
|
||||
stringRootSlashSlash = dirOf "///";
|
||||
stringRootA = dirOf "/a";
|
||||
stringWithDot = dirOf "a/b/c/./d";
|
||||
stringWithDotSep2 = dirOf "a/b/c/.//d";
|
||||
stringWithDotDot = dirOf "a/b/c/../d";
|
||||
stringWithDotDotSep2 = dirOf "a/b/c/..//d";
|
||||
stringWithDotAndDotDot = dirOf "a/b/c/.././d";
|
||||
stringWithDotAndDotDotSep2 = dirOf "a/b/c/.././/d";
|
||||
|
||||
pathRoot = dirOf /.;
|
||||
pathDoesntExistRoot = dirOf /totallydoesntexistreally;
|
||||
pathDoesntExistNested1 = dirOf /totallydoesntexistreally/subdir1;
|
||||
pathDoesntExistNested2 = dirOf /totallydoesntexistreally/subdir1/subdir2;
|
||||
}
|
||||
@@ -29,6 +29,18 @@ nix-build -o "$TEST_ROOT"/result dependencies.nix --post-build-hook "$pushToStor
|
||||
export BUILD_HOOK_ONLY_OUT_PATHS=$([ ! "$NIX_TESTS_CA_BY_DEFAULT" ])
|
||||
nix-build -o "$TEST_ROOT"/result-mult multiple-outputs.nix -A a.first --post-build-hook "$pushToStore"
|
||||
|
||||
if isDaemonNewer "2.33.0pre20251029"; then
|
||||
# Regression test for issue #14287: `--check` should re-run post build
|
||||
# hook, even though nothing is getting newly registered.
|
||||
export HOOK_DEST=$TEST_ROOT/listing
|
||||
# Needed so the hook will get the above environment variable.
|
||||
restartDaemon
|
||||
nix-build -o "$TEST_ROOT"/result-mult multiple-outputs.nix --check -A a.first --post-build-hook "$PWD/build-hook-list-paths.sh"
|
||||
grepQuiet a-first "$HOOK_DEST"
|
||||
grepQuiet a-second "$HOOK_DEST"
|
||||
unset HOOK_DEST
|
||||
fi
|
||||
|
||||
clearStore
|
||||
|
||||
# Ensure that the remote store contains both the runtime and build-time
|
||||
|
||||
@@ -82,4 +82,8 @@ mkDerivation {
|
||||
"foo$" = "BAD";
|
||||
|
||||
exportReferencesGraph.refs = [ dep ];
|
||||
exportReferencesGraph.refs2 = [
|
||||
dep
|
||||
[ dep ]
|
||||
]; # regression test for heterogeneous arrays
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
source common.sh
|
||||
|
||||
# 27ce722638 required some incompatible changes to the nix file, so skip this
|
||||
# tests for the older versions
|
||||
requireDaemonNewerThan "2.4pre20210712"
|
||||
# https://github.com/NixOS/nix/pull/14189
|
||||
requireDaemonNewerThan "2.33"
|
||||
|
||||
clearStoreIfPossible
|
||||
|
||||
|
||||
Reference in New Issue
Block a user