Compare commits

...

56 Commits

Author SHA1 Message Date
Eelco Dolstra
aa657c1679 Revert "Merge pull request #14382 from NixOS/backport-14364-to-2.32-maintenance"
This reverts commit 5c9481de19,
reversing changes made to
291e8ab6bd. This is a behaviour change
that should be avoided on maintenance branches.
2025-11-06 22:10:42 +01:00
internal-nix-ci[bot]
a4fb83a239 Merge pull request #14498 from NixOS/backport-14491-to-2.32-maintenance
[Backport 2.32-maintenance] Don't crash on flakerefs containing newlines
2025-11-06 19:38:36 +00:00
Eelco Dolstra
47ba375285 Don't crash on flakerefs containing newlines
Fixes #14311.

(cherry picked from commit c1317017e9)
2025-11-06 19:18:16 +00:00
internal-nix-ci[bot]
5c9e22d75a Merge pull request #14485 from NixOS/backport-14482-to-2.32-maintenance
[Backport 2.32-maintenance] nix flake check: Remove incorrect assertion
2025-11-05 20:23:17 +00:00
Eelco Dolstra
e6d823e46d nix flake check: Remove incorrect assertion
The assumption that no unknown paths can be returned is incorrect. It
can happen if a derivation has outputs that are substitutable, but
that have references that cannot be substituted (i.e. an incomplete
closure in the binary cache). This can easily happen with
magic-nix-cache.

(cherry picked from commit a828cf777a)
2025-11-05 19:51:29 +00:00
internal-nix-ci[bot]
038cc7913b Merge pull request #14461 from NixOS/backport-14450-to-2.32-maintenance
[Backport 2.32-maintenance] flake: Update, nixos-25.05-small -> nixos-25.05
2025-11-03 17:43:37 +00:00
Robert Hensing
828bf74cd0 Apply updated nixfmt
(cherry picked from commit 81a2809a52)
2025-11-03 17:08:34 +00:00
Robert Hensing
ec122cbfda flake: Update, nixos-25.05-small -> nixos-25.05
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/d98ce345cdab58477ca61855540999c86577d19d?narHash=sha256-O2CIn7HjZwEGqBrwu9EU76zlmA5dbmna7jL1XUmAId8%3D' (2025-08-26)
  → 'github:NixOS/nixpkgs/daf6dc47aa4b44791372d6139ab7b25269184d55?narHash=sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8%2BON/0Yy8%2Ba5vsDU%3D' (2025-10-27)

(cherry picked from commit 233bd250d1)
2025-11-03 17:08:34 +00:00
internal-nix-ci[bot]
e1ff27324b Merge pull request #14438 from NixOS/backport-14434-to-2.32-maintenance
[Backport 2.32-maintenance] libstore: Improve store-reference back-compat with IPv6 ZoneId literals
2025-11-01 00:18:55 +00:00
Sergei Zimmerman
7b41563055 libstore: Improve store-reference back-compat with IPv6 ZoneId literals
This restores the pre-2.31 handling of ZoneID identifiers in store references.
It's the only place we reasonably care about this back-compat.

(cherry picked from commit 8dbc2475f7)
2025-10-31 23:50:22 +00:00
internal-nix-ci[bot]
7f9b9c3638 Merge pull request #14436 from NixOS/backport-14431-to-2.32-maintenance
[Backport 2.32-maintenance] libfetchers: Restore plain git inputs recognition
2025-10-31 23:25:25 +00:00
Sergei Zimmerman
ed09f1b4d9 libfetchers: Restore plain git inputs recognition
Accidentally broken in dbc235cc62.
Adds a bit of tests for this, even though this protocol is mostly deprecated
everywhere.

(cherry picked from commit ade3d5d746)
2025-10-31 23:01:54 +00:00
internal-nix-ci[bot]
fc6811cb51 Merge pull request #14435 from NixOS/backport-14432-to-2.32-maintenance
[Backport 2.32-maintenance] meson: Also split version string at '+' for Darwin
2025-10-31 22:24:18 +00:00
Sergei Zimmerman
a24df3d4e5 meson: Also split version string at '+' for Darwin
(cherry picked from commit 1ca6e9ef54)
2025-10-31 21:47:27 +00:00
internal-nix-ci[bot]
e6003b5c4f Merge pull request #14430 from NixOS/backport-14137-to-2.32-maintenance
[Backport 2.32-maintenance] fix(libstore/build/derivation-goal): don't assert on partially valid outputs
2025-10-31 20:59:43 +00:00
Bernardo Meurer Costa
f566957dc4 fix(libstore/build/derivation-goal): don't assert on partially valid outputs
Fixes: #14130
(cherry picked from commit 9eecee3d4e)
2025-10-31 23:30:03 +03:00
internal-nix-ci[bot]
f434a3e3c6 Merge pull request #14413 from NixOS/backport-14410-to-2.32-maintenance
[Backport 2.32-maintenance] zsh/completion: put compdef on first line
2025-10-29 16:17:24 +00:00
bryango
939f81c2e6 zsh/completion: put compdef on first line
Some zsh setups (including mine) do not load the
completion if `#compdef` is not on the first line.

So we move the `# shellcheck` comment to the
second line to avoid this issue.

(cherry picked from commit 956fffdd6f)
2025-10-29 15:58:06 +00:00
internal-nix-ci[bot]
758dacacf4 Merge pull request #14409 from NixOS/backport-14289-to-2.32-maintenance
[Backport 2.32-maintenance] Fix issue #14287
2025-10-29 08:29:01 +00:00
John Ericson
9e4177bc67 Fix issue #14287
The test added in the previous commit now passes.

Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
(cherry picked from commit de192794c9)
2025-10-29 07:59:08 +00:00
John Ericson
328a3bbbd0 Regression test for issue #14287
This will currently fail, until the bug is fixed.

Co-Authored-By: Sergei Zimmerman <sergei@zimmerman.foo>
(cherry picked from commit 246dbe1c05)
2025-10-29 07:59:08 +00:00
internal-nix-ci[bot]
5c9481de19 Merge pull request #14382 from NixOS/backport-14364-to-2.32-maintenance
[Backport 2.32-maintenance] diff-closures: print sizes with dynamic unit
2025-10-27 20:52:05 +00:00
Marcel
ebadea0734 treewide: replace manual MiB calculations with renderSize
(cherry picked from commit 584a8e8a00)
2025-10-27 19:43:40 +00:00
Marcel
7d7ca3fe96 refactor(libutil): remove showBytes() in favor of renderSize()
The `showBytes()` function was redundant with `renderSize()` as the
latter automatically selects the appropriate unit (KiB, MiB, GiB, etc.)
based on the value, whereas `showBytes()` always formatted as MiB
regardless of size.

Co-authored-by: Bernardo Meurer Costa <beme@anthropic.com>
(cherry picked from commit f234633e27)
2025-10-27 19:43:40 +00:00
Marcel
3a92f83e75 diff-closures: print sizes with dynamic unit
(cherry picked from commit 9d4d10954a)
2025-10-27 19:43:40 +00:00
Eelco Dolstra
291e8ab6bd Merge pull request #14372 from NixOS/backport-14362-to-2.32-maintenance
[Backport 2.32-maintenance] libexpr: Speed up BindingsBuilder::finishSizeIfNecessary
2025-10-27 16:41:28 +01:00
Eelco Dolstra
19441dd317 Bump version 2025-10-27 16:36:00 +01:00
internal-nix-ci[bot]
71ec2cf62d Merge pull request #14371 from NixOS/backport-14340-to-2.32-maintenance
[Backport 2.32-maintenance] nix-2.32 needs boost-1.87+ for `try_emplace_and_cvisit`
2025-10-27 15:01:12 +00:00
Sergei Zimmerman
b36f8043d2 libexpr: Speed up BindingsBuilder::finishSizeIfNecessary
Instead of iterating over the newly built bindings we can
do a cheaper set_intersection to count duplicates or fall back
to a per-element binary search over the "base" bindings.

This speeds up `hello` evaluation by around 10ms (0.196s -> 0.187s) and
`nixos.closures.ec2.x86_64-linux` by 140ms (2.744s -> 2.609s).

This addresses a somewhat steep performance regression from 82315c3807
that reduced memory requirements of attribute set merges. With this patch
we get back around to 2.31 level of eval performance while keeping the memory
usage optimization.

Also document the optimization a bit more.

(cherry picked from commit ec2fd2dc23)
2025-10-27 14:40:17 +00:00
Jens Petersen
4d1f72a324 libexpr needs boost-1.87+ for try_emplace_and_cvisit
Since 2.32, nix now needs boost 1.87 or later to build,
due to using unordered::concurrent_flat_map try_emplace_and_cvisit

../src/libexpr/eval.cc: In member function ‘void nix::EvalState::evalFile(const nix::SourcePath&, nix::Value&, bool)’:
../src/libexpr/eval.cc:1096:20: error: ‘class boost::unordered::concurrent_flat_map<nix::SourcePath, nix::Value*, std::hash<nix::SourcePath>, std::equal_to<nix::SourcePath>, traceable_allocator<std::pair<const nix::SourcePath, nix::Value*> > >’ has no member named ‘try_emplace_and_cvisit’; did you mean ‘try_emplace_or_cvisit’?
 1096 |     fileEvalCache->try_emplace_and_cvisit(
      |                    ^~~~~~~~~~~~~~~~~~~~~~
      |                    try_emplace_or_cvisit

See 834580b539

(cherry picked from commit f594a8e11e)
2025-10-27 14:28:38 +00:00
internal-nix-ci[bot]
ac3532d0f2 Merge pull request #14354 from NixOS/backport-14343-to-2.32-maintenance
[Backport 2.32-maintenance] Revert "libmain: Catch logger exceptions in `handleExceptions`"
2025-10-25 00:00:15 +00:00
Sergei Zimmerman
84dbf182d4 Revert "libmain: Catch logger exceptions in handleExceptions"
This reverts commit 90d1ff4805.

The initial issue with EPIPE was solved in 9f680874c5.
Now this patch does move bad than good by eating up boost::io::format_error that are
bugs.

(cherry picked from commit 4f5af471fb)
2025-10-24 23:28:53 +00:00
internal-nix-ci[bot]
4a27d70132 Merge pull request #14280 from NixOS/backport-14276-to-2.32-maintenance
[Backport 2.32-maintenance] libstore/registerOutputs: Don't try to optimize a non-existent actual…
2025-10-16 22:17:53 +00:00
Sergei Zimmerman
dadb5b01b7 libstore/registerOutputs: Don't try to optimize a non-existent actualPath
Since 3c610df550 this resulted in `getting status of`
errors on paths inside the chroot if a path was already valid. Careful inspection
of the logic shows that if buildMode != bmCheck actualPath gets reassigned to
store.toRealPath(finalDestPath). The only branch that cares about actualPath is
the buildMode == bmCheck case, which doesn't lead to optimisePath anyway.

(cherry picked from commit 4cbcaad435)
2025-10-16 21:46:08 +00:00
internal-nix-ci[bot]
3c39583e55 Merge pull request #14267 from NixOS/backport-14263-to-2.32-maintenance
[Backport 2.32-maintenance] Restore `ServeProto::Command::ImportPaths`
2025-10-16 00:07:32 +00:00
Sergei Zimmerman
a038c92d38 Restore ServeProto::Command::ImportPaths
This partially reverts commit 5e46df973f,
partially reversing changes made to
8c789db05b.

We do this because Hydra, while using the newer version of the protocol,
still uses this command, even though Nix (as a client) doesn't use it.
On that basis, we don't want to remove it (or consider it only part of
the older versions of the protocol) until Hydra no longer uses the
Legacy SSH Protocol.

(cherry picked from commit 0deb492b3d)
2025-10-15 23:38:26 +00:00
internal-nix-ci[bot]
cf6ad228ae Merge pull request #14259 from NixOS/backport-14253-to-2.32-maintenance
[Backport 2.32-maintenance] libfetchers/git-utils: Be more correct about validating refnames
2025-10-15 20:33:35 +00:00
Sergei Zimmerman
44701007b4 libfetchers/git-utils: Be more correct about validating refnames
Turns out there's a much better API for this that doesn't have the
footguns of the previous method.

isLegalRefName is somewhat of a misnomer, since it's mainly used to
validate user inputs that can be either references, branch names,
psedorefs or tags.

(cherry picked from commit 5d1178b817)
2025-10-15 20:08:43 +00:00
internal-nix-ci[bot]
ff1f145992 Merge pull request #14256 from NixOS/backport-14205-to-2.32-maintenance
[Backport 2.32-maintenance] Improved backwards compatibility hack for git URLs using dir=...
2025-10-15 16:31:21 +00:00
Graham Dennis
3519ad2ca6 Improve comment
(cherry picked from commit 8d9e9bc400)
2025-10-15 15:58:18 +00:00
Graham Dennis
16af6a8ed1 Improved backwards compatibility hack for git URLs using dir=... attribute
(cherry picked from commit 43b01b6790)
2025-10-15 15:58:18 +00:00
Eelco Dolstra
549a2e8272 Bump version 2025-10-14 11:26:39 +02:00
internal-nix-ci[bot]
2531dcad75 Merge pull request #14238 from NixOS/backport-14237-to-2.32-maintenance
[Backport 2.32-maintenance] Remove validation of URLs passed to FileTransferRequest verbatim
2025-10-13 21:23:05 +00:00
Sergei Zimmerman
11f9c59140 Remove validation of URLs passed to FileTransferRequest verbatim
CURL is not very strict about validation of URLs passed to it. We
should reflect this in our handling of URLs that we get from the user
in <nix/fetchurl.nix> or builtins.fetchurl. ValidURL was an attempt to
rectify this, but it turned out to be too strict. The only good way to
resolve this is to pass (in some cases) the user-provided string verbatim
to CURL. Other usages in libfetchers still benefit from using structured
ParsedURL and validation though.

nix store prefetch-file --name foo 'https://cdn.skypack.dev/big.js@^5.2.2'
error: 'https://cdn.skypack.dev/big.js@^5.2.2' is not a valid URL: leftover

(cherry picked from commit 47f427a172)
2025-10-13 20:48:15 +00:00
internal-nix-ci[bot]
a25a219e79 Merge pull request #14213 from NixOS/backport-14194-to-2.32-maintenance
[Backport 2.32-maintenance] libutil: Print stack trace on assertion failure
2025-10-11 00:40:50 +00:00
Sergei Zimmerman
f07486b205 libutil: Print stack trace on assertion failure
This change overrides __assert_fail on glibc/musl
to instead call std::terminate that we have a custom
handler for. This ensures that we have more context
to diagnose issues encountered by users in the wild.

(cherry picked from commit 46382ade74)
2025-10-11 00:08:36 +00:00
internal-nix-ci[bot]
9ec98f7844 Merge pull request #14212 from NixOS/backport-14210-to-2.32-maintenance
[Backport 2.32-maintenance] libstore: Fix double-quoting of paths in logs
2025-10-10 23:26:18 +00:00
Sergei Zimmerman
634e1d3b65 libstore: Fix double-quoting of paths in logs
std::filesystem::path is already quoted by boost::format with double quotes (").
(cherry picked from commit f30cb8667b)
2025-10-10 22:54:22 +00:00
internal-nix-ci[bot]
70655061e3 Merge pull request #14202 from NixOS/backport-14199-to-2.32-maintenance
[Backport 2.32-maintenance] packaging: only override `toml11` when necessary
2025-10-09 18:04:05 +00:00
Seth Flynn
da328e6004 packaging: only override toml11 when necessary
v4.4.0 hit Nixpkgs in https://github.com/NixOS/nixpkgs/pull/442682.
Ideally we'd just use that, but this keeps the fallback behavior until
it's more widespread

(cherry picked from commit 0f016f9bf5)
2025-10-09 17:56:10 +00:00
internal-nix-ci[bot]
6b16af8c0e Merge pull request #14197 from NixOS/backport-14191-to-2.32-maintenance
[Backport 2.32-maintenance] libutil: Fix renderAuthorityAndPath unreachable for path:/ URLs
2025-10-09 00:00:28 +00:00
internal-nix-ci[bot]
010b78e0cf Merge pull request #14196 from NixOS/backport-14189-to-2.32-maintenance
[Backport 2.32-maintenance] exportReferencesGraph: Handle heterogeneous arrays
2025-10-08 23:43:51 +00:00
Sergei Zimmerman
98b7654390 libutil: Fix renderAuthorityAndPath unreachable for path:/ URLs
This was mistakenly triggered by path:/ URL, since the `//` would
correspond to 3 empty segments.

(cherry picked from commit 1d8dd77e1d)
2025-10-08 23:24:01 +00:00
Eelco Dolstra
c5799aa62c exportReferencesGraph: Handle heterogeneous arrays
This barfed with

   error: [json.exception.type_error.302] type must be string, but is array

on `nix build github:malt3/bazel-env#bazel-env` because it has a `exportReferencesGraph` with a value like `["string",...["string"]]`.

(cherry picked from commit 94f410b628)
2025-10-08 23:13:09 +00:00
Eelco Dolstra
72e3dd396c Bump version 2025-10-07 17:14:49 +02:00
Eelco Dolstra
d069633b3d Mark official release 2025-10-07 13:30:16 +02:00
50 changed files with 483 additions and 167 deletions

View File

@@ -1 +1 @@
2.32.0
2.32.3

View File

@@ -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
View File

@@ -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"
}

View File

@@ -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 = [

View File

@@ -1,5 +1,5 @@
# shellcheck disable=all
#compdef nix
# shellcheck disable=all
function _nix() {
local ifs_bk="$IFS"

View 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

View File

@@ -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)));
}

View File

@@ -42,5 +42,7 @@ 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]
# Darwin ld doesn't like "X.Y.ZpreABCD+W"
nix_soversion = meson.project_version().split('+')[0].split('pre')[0]
subdir('assert-fail')

View File

@@ -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 =

View File

@@ -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:

View File

@@ -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"));

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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.

View File

@@ -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());

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0]?a=b&c=d

View File

@@ -0,0 +1 @@
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]?a=b&c=d

View File

@@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25?a=b&c=d

View File

@@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d

View File

@@ -0,0 +1 @@
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d

View File

@@ -0,0 +1 @@
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0

View File

@@ -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

View File

@@ -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);

View File

@@ -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));
}
}
}

View File

@@ -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);

View File

@@ -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", "");

View File

@@ -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())
{

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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 =

View File

@@ -1717,7 +1717,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 +1730,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
auto destPath = store.toRealPath(finalDestPath);
deletePath(destPath);
movePath(actualPath, destPath);
actualPath = destPath;
}
}
@@ -1784,7 +1782,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;

View File

@@ -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 = {"", ""},

View File

@@ -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

View File

@@ -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`.

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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);
}

View 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

View File

@@ -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'

View File

@@ -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'"

View File

@@ -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

View File

@@ -82,4 +82,8 @@ mkDerivation {
"foo$" = "BAD";
exportReferencesGraph.refs = [ dep ];
exportReferencesGraph.refs2 = [
dep
[ dep ]
]; # regression test for heterogeneous arrays
}

View File

@@ -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