Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4d1f72a
libexpr needs boost-1.87+ for try_emplace_and_cvisit
juhp Oct 23, 2025
b36f804
libexpr: Speed up BindingsBuilder::finishSizeIfNecessary
xokdvium Oct 27, 2025
71ec2cf
Merge pull request #14371 from NixOS/backport-14340-to-2.32-maintenance
internal-nix-ci[bot] Oct 27, 2025
19441dd
Bump version
edolstra Oct 27, 2025
291e8ab
Merge pull request #14372 from NixOS/backport-14362-to-2.32-maintenance
edolstra Oct 27, 2025
3a92f83
diff-closures: print sizes with dynamic unit
MarcelCoding Oct 26, 2025
7d7ca3f
refactor(libutil): remove `showBytes()` in favor of `renderSize()`
MarcelCoding Oct 26, 2025
ebadea0
treewide: replace manual MiB calculations with renderSize
MarcelCoding Oct 27, 2025
5c9481d
Merge pull request #14382 from NixOS/backport-14364-to-2.32-maintenance
internal-nix-ci[bot] Oct 27, 2025
328a3bb
Regression test for issue #14287
Ericson2314 Oct 17, 2025
9e4177b
Fix issue #14287
Ericson2314 Oct 17, 2025
758daca
Merge pull request #14409 from NixOS/backport-14289-to-2.32-maintenance
internal-nix-ci[bot] Oct 29, 2025
939f81c
zsh/completion: put compdef on first line
bryango Oct 29, 2025
f434a3e
Merge pull request #14413 from NixOS/backport-14410-to-2.32-maintenance
internal-nix-ci[bot] Oct 29, 2025
f566957
fix(libstore/build/derivation-goal): don't assert on partially valid …
lovesegfault Oct 1, 2025
e6003b5
Merge pull request #14430 from NixOS/backport-14137-to-2.32-maintenance
internal-nix-ci[bot] Oct 31, 2025
a24df3d
meson: Also split version string at '+' for Darwin
xokdvium Oct 31, 2025
fc6811c
Merge pull request #14435 from NixOS/backport-14432-to-2.32-maintenance
internal-nix-ci[bot] Oct 31, 2025
ed09f1b
libfetchers: Restore plain git inputs recognition
xokdvium Oct 31, 2025
7f9b9c3
Merge pull request #14436 from NixOS/backport-14431-to-2.32-maintenance
internal-nix-ci[bot] Oct 31, 2025
7b41563
libstore: Improve store-reference back-compat with IPv6 ZoneId literals
xokdvium Oct 31, 2025
e1ff273
Merge pull request #14438 from NixOS/backport-14434-to-2.32-maintenance
internal-nix-ci[bot] Nov 1, 2025
ec122cb
flake: Update, nixos-25.05-small -> nixos-25.05
roberth Nov 2, 2025
828bf74
Apply updated nixfmt
roberth Nov 3, 2025
038cc79
Merge pull request #14461 from NixOS/backport-14450-to-2.32-maintenance
internal-nix-ci[bot] Nov 3, 2025
e6d823e
nix flake check: Remove incorrect assertion
edolstra Nov 5, 2025
5c9e22d
Merge pull request #14485 from NixOS/backport-14482-to-2.32-maintenance
internal-nix-ci[bot] Nov 5, 2025
47ba375
Don't crash on flakerefs containing newlines
edolstra Nov 6, 2025
a4fb83a
Merge pull request #14498 from NixOS/backport-14491-to-2.32-maintenance
internal-nix-ci[bot] Nov 6, 2025
aa657c1
Revert "Merge pull request #14382 from NixOS/backport-14364-to-2.32-m…
edolstra Nov 6, 2025
cd898c3
Merge tag '2.32.3' into sync-2.32.3
edolstra Nov 7, 2025
2fadb19
flake.lock: Update
edolstra Nov 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.32.2
2.32.3
6 changes: 3 additions & 3 deletions doc/manual/generate-store-types.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion misc/zsh/completion.zsh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# shellcheck disable=all
#compdef nix
# shellcheck disable=all

function _nix() {
local ifs_bk="$IFS"
Expand Down
4 changes: 2 additions & 2 deletions nix-meson-build-support/common/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +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')
45 changes: 41 additions & 4 deletions src/libexpr/include/nix/expr/attr-set.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 1 addition & 2 deletions src/libfetchers/git.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions src/libflake-tests/flakeref.cc
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,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.
Expand Down
3 changes: 2 additions & 1 deletion src/libflake/flakeref.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0]?a=b&c=d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://userinfo@[fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25eth0]?a=b&c=d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%25?a=b&c=d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://userinfo@fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0?a=b&c=d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh://fea5:23e1:3916:fc24:cb52:2837:2ecb:ea8e%eth0
60 changes: 60 additions & 0 deletions src/libstore-tests/store-reference.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 9 additions & 1 deletion src/libstore/build/derivation-building-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,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);
Expand Down
14 changes: 13 additions & 1 deletion src/libstore/build/derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}

Expand Down
28 changes: 26 additions & 2 deletions src/libstore/store-reference.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,39 @@ 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) {
fixedAuthority += *userinfo;
fixedAuthority += '@';
}
fixedAuthority += '[';
fixedAuthority += authorityString;
fixedAuthority += maybeIpv6S;
if (maybeZoneId) {
fixedAuthority += "%25"; // pct-encoded percent character
fixedAuthority += *maybeZoneId;
}
fixedAuthority += ']';
return {
.variant =
Expand Down
2 changes: 1 addition & 1 deletion src/libutil/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
12 changes: 12 additions & 0 deletions tests/functional/build-hook-list-paths.sh
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions tests/functional/post-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down