Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nix for openSUSE:Factory checked in at 2026-10-14 18:35:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nix (Old) and /work/SRC/openSUSE:Factory/.nix.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nix" Wed Oct 14 18:35:27 2026 rev:23 rq:1384673 version:2.35.3 Changes: -------- --- /work/SRC/openSUSE:Factory/nix/nix.changes 2026-08-14 22:07:27.519048271 +0200 +++ /work/SRC/openSUSE:Factory/.nix.new.3698736/nix.changes 2026-10-14 18:36:09.356480799 +0200 @@ -1,0 +2,90 @@ +Tue Oct 13 15:31:33 UTC 2026 - Marcus Rueckert <[email protected]> + +- Update to version 2.35.3: + - 10d9e06fe Fix sandbox breakpoint escape + - 4cfab486f Merge remote-tracking branch + 'nix-ghsa-hf6w-2rg9-v84g/fix-installer-2.35' into + mitigations-2.35 + - 224b720f2 Merge remote-tracking branch + 'nix-ghsa-jwvx-xq5j-gc56/restrict-repair-flag-to-trusted-users-2.35' + into mitigations-2.35 + - cd9020f77 Merge remote-tracking branch + 'nix-ghsa-92x5-h528-wrmw/reject-build-users-group-2.35' into + mitigations-2.35 + - 3755b3b73 Merge remote-tracking branch + 'nix-ghsa-25vp-6h23-hj3g/fix-gen-list-overflows-2.35' into + mitigations-2.35 + - 7e675b31a [Backport 2.35-maintenance] libstore: treat `\r\n` as + a single line terminator in build logs (#16599) + - c4807b4a1 libstore: treat `\r\n` as a single line terminator in + build logs + - 623bbfd94 installer: Fix store object ownership in multi-user + installs + - a3ec144fa [Backport 2.35-maintenance] installer: Fix store + mtime in binary tarballs (#16559) + - b07ef7c01 installer: Fix store mtime in binary tarballs + - f4dd3bda8 tests/installer: Refactor to support custom checks + and mirror nix-installer repo + - 1a202f9dc tests/installer: Set -u in the tests + - 56f147d2e [Backport 2.35-maintenance] gc-closure.sh: avoid + SIGPIPE from head (#16557) + - c0cb11d58 gc-closure.sh: avoid SIGPIPE from head + - 355240b45 libstore: Unpack to temporary location in + LocalStore::addToStore + - 2252caebf libstore/daemon: Disallow unprivileged repair in + AddMultipleToStore/AddToStoreNar/AddToStore + - 48d8bc136 Record gid in getPeerInfo when using LOCAL_PEERCRED + - 83f4ac399 Merge pull request #16511 from + NixOS/backport-16229-to-2.35-maintenance + - 0df979fe0 Fix ignored nodiscard results + - 1ad4a52d3 Merge pull request #16489 from + NixOS/backport-16487-to-2.35-maintenance + - 528e705b8 nix flake prefetch-inputs: skip relative path inputs + - d7c4344e4 libexpr: Tighten allocBytes with overload that does + unsigned overflow detection + - 88c2bcc16 Merge pull request #16464 from + NixOS/backport-16067-to-2.35-maintenance + - a5a93f8e8 libstore: detect Rosetta via runtime file instead of + spawning arch + - a40dd52cf Merge pull request #16419 from + NixOS/backport-16418-to-2.35-maintenance + - beed3dd4a fix(packaging): do not add boost fiber patch if + duplicate + - e69fa58ed Merge pull request #16410 from + NixOS/backport-16407-to-2.35-maintenance + - 413162760 libexpr-c: allow null primop docs + - 8e94a93ec Merge pull request #16405 from + NixOS/backport-16403-to-2.35-maintenance + - c39e300db Convert stdenv.isLinux to stdenv.hostPlatform.isLinux + - 0b726cab9 Merge pull request #16402 from + NixOS/backport-16397-to-2.35-maintenance + - 878674493 Merge pull request #16401 from + NixOS/backport-16391-to-2.35-maintenance + - 81dca68c7 libfetchers/git: render submodules as empty dirs in + workdir accessor + - 9c5d36a0c libfetchers/git: use HEAD as default ref for detached + local repos + - 553bac087 Merge pull request #16379 from + NixOS/backport-16373-to-2.35-maintenance + - d6f5cef6c nix flake prefetch-inputs: fix inputs being randomly + skipped + - 8eb4cf95c Merge pull request #16351 from + NixOS/backport-16190-to-2.35-maintenance + - b8abc0832 Populate the srcToStore cache when we have a fetcher + cache hit + - cf5200adb Merge pull request #16350 from + NixOS/backport-16295-to-2.35-maintenance + - de3a27708 Improve 'sandbox-fallback' diagnostics, disallow + fallback with relocated store + - c138b869a Merge pull request #16331 from + JamieMagee/backport-16325-to-2.35-maintenance + - 4284b5768 Validate padding after skipped NAR contents + - 939898e5f Merge pull request #16319 from + NixOS/backport-fix-16317 + - 76c15a273 libstore: Merge, don't clobber, colliding `inputDrvs` + hashes modulo + - b87548075 Merge pull request #16304 from + NixOS/backport-16303-to-2.35-maintenance + - 1e4f38765 rl-2.35.md: correct meson option to disable mimalloc + +------------------------------------------------------------------- Old: ---- nix-2.35.2.tar.gz New: ---- nix-2.35.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nix.spec ++++++ --- /var/tmp/diff_new_pack.CCUDB4/_old 2026-10-14 18:36:10.414525346 +0200 +++ /var/tmp/diff_new_pack.CCUDB4/_new 2026-10-14 18:36:10.415525388 +0200 @@ -26,7 +26,7 @@ %bcond_with docs Name: nix -Version: 2.35.2 +Version: 2.35.3 Release: 0 Summary: The purely functional package manager License: LGPL-2.1-only ++++++ nix-2.35.2.tar.gz -> nix-2.35.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/.version new/nix-2.35.3/.version --- old/nix-2.35.2/.version 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/.version 2026-10-13 13:32:25.000000000 +0200 @@ -1 +1 @@ -2.35.2 +2.35.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/doc/manual/source/release-notes/rl-2.35.md new/nix-2.35.3/doc/manual/source/release-notes/rl-2.35.md --- old/nix-2.35.2/doc/manual/source/release-notes/rl-2.35.md 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/doc/manual/source/release-notes/rl-2.35.md 2026-10-13 13:32:25.000000000 +0200 @@ -61,7 +61,7 @@ The `nix` binary now links [mimalloc](https://github.com/microsoft/mimalloc) by default, replacing glibc's malloc for all non-GC allocations. This yields a **5–12% wall-clock improvement** on evaluation workloads, ranging from `nix-instantiate hello` to `nix-env -qa` and full NixOS configurations. - The allocator can be disabled at build time with `-Dmimalloc=disabled`. + The allocator can be disabled at build time with `-Dnix:mimalloc=disabled`. - The `revCount` attribute of the Git fetchers is now lazily computed and passed-through as-is when explicitly specified [#15772](https://github.com/NixOS/nix/pull/15772) [#14596](https://github.com/NixOS/nix/pull/14596) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/packaging/binary-tarball.nix new/nix-2.35.3/packaging/binary-tarball.nix --- old/nix-2.35.2/packaging/binary-tarball.nix 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/packaging/binary-tarball.nix 2026-10-13 13:32:25.000000000 +0200 @@ -79,10 +79,11 @@ fn=$out/$dir.tar.xz mkdir -p $out/nix-support echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products + # Store mtime is 1 second into the epoch. tar cf - \ --sort=name \ --owner=0 --group=0 --mode=u+rw,uga+r \ - --mtime='1970-01-01' \ + --mtime='@1' \ --absolute-names \ --hard-dereference \ --transform "s,$TMPDIR/install,$dir/install," \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/packaging/dependencies.nix new/nix-2.35.3/packaging/dependencies.nix --- old/nix-2.35.2/packaging/dependencies.nix 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/packaging/dependencies.nix 2026-10-13 13:32:25.000000000 +0200 @@ -98,9 +98,14 @@ "--with-iostreams" "--with-url" ]; - patches = [ - ./patches/0001-Fix-uncaught_exceptions-not-accounting-for-forced_un.patch - ]; + patches = lib.optional ( + lib.versionAtLeast pkgs.boost.version "1.88" + && lib.versionOlder pkgs.boost.version "1.92" + # may already be done in nixpkgs: https://github.com/NixOS/nixpkgs/pull/546405 + && !lib.any (patch: lib.hasInfix "5883212311535a0046031d74d1568ae173c1e35b" (baseNameOf patch)) ( + pkgs.boost.patches or [ ] + ) + ) ./patches/0001-Fix-uncaught_exceptions-not-accounting-for-forced_un.patch; enableIcu = false; }).overrideAttrs (old: { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/packaging/everything.nix new/nix-2.35.3/packaging/everything.nix --- old/nix-2.35.2/packaging/everything.nix 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/packaging/everything.nix 2026-10-13 13:32:25.000000000 +0200 @@ -161,7 +161,7 @@ ln -sT ${nix-manual} $doc ln -sT ${nix-manual.man} $man '' - + lib.optionalString stdenv.isLinux '' + + lib.optionalString stdenv.hostPlatform.isLinux '' lndir ${nix-nswrapper} $out ''; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/packaging/rust-installer/tarball.nix new/nix-2.35.3/packaging/rust-installer/tarball.nix --- old/nix-2.35.2/packaging/rust-installer/tarball.nix 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/packaging/rust-installer/tarball.nix 2026-10-13 13:32:25.000000000 +0200 @@ -36,10 +36,11 @@ cp ${installerClosureInfo}/registration $TMPDIR/reginfo + # Store mtime is 1 second into the epoch. tar cf - \ --sort=name \ --owner=0 --group=0 --mode=u+rw,uga+r \ - --mtime='1970-01-01' \ + --mtime='@1' \ --absolute-names \ --hard-dereference \ --transform "s,$TMPDIR/reginfo,$dir/.reginfo," \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/scripts/install-multi-user.sh new/nix-2.35.3/scripts/install-multi-user.sh --- old/nix-2.35.2/scripts/install-multi-user.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/scripts/install-multi-user.sh 2026-10-13 13:32:25.000000000 +0200 @@ -864,6 +864,9 @@ cp -RP --preserve=ownership,timestamps ./store/* "$NIX_ROOT/store/" fi + _sudo "to chown the $NIX_ROOT/store contents" \ + chown -hR 0:0 "$NIX_ROOT"/store/* + _sudo "to make the new store non-writable at $NIX_ROOT/store" \ chmod -R ugo-w "$NIX_ROOT/store/" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/scripts/install-nix-from-tarball.sh new/nix-2.35.3/scripts/install-nix-from-tarball.sh --- old/nix-2.35.2/scripts/install-nix-from-tarball.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/scripts/install-nix-from-tarball.sh 2026-10-13 13:32:25.000000000 +0200 @@ -187,6 +187,10 @@ mv "$i_tmp" "$dest/store/$i" chmod -w "$dest/store/$i" fi + # BusyBox tar is bad at preserving mtime because it doesn't seem to do the correct + # thing of doing a final fixup pass for fixing parent directory permissions and doesn't + # handle symlinks at all. So we can set the correct mtime here. + find "$dest/store/$i" -depth -exec touch -hmd "@1" {} + done echo "" >&2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libcmd/unix/unix-socket-server.cc new/nix-2.35.3/src/libcmd/unix/unix-socket-server.cc --- old/nix-2.35.2/src/libcmd/unix/unix-socket-server.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libcmd/unix/unix-socket-server.cc 2026-10-13 13:32:25.000000000 +0200 @@ -49,8 +49,10 @@ xucred cred; socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) + if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) { peer.uid = cred.cr_uid; + peer.gid = cred.cr_gid; + } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr/attr-set.cc new/nix-2.35.3/src/libexpr/attr-set.cc --- old/nix-2.35.2/src/libexpr/attr-set.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr/attr-set.cc 2026-10-13 13:32:25.000000000 +0200 @@ -18,7 +18,7 @@ throw Error("attribute set of size %d is too big", capacity); stats.nrAttrsets++; stats.nrAttrsInAttrsets += capacity; - return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings(); + return new (allocBytes<Attr, sizeof(Bindings)>(capacity)) Bindings(); } Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr/eval.cc new/nix-2.35.3/src/libexpr/eval.cc --- old/nix-2.35.2/src/libexpr/eval.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr/eval.cc 2026-10-13 13:32:25.000000000 +0200 @@ -87,9 +87,7 @@ StringData & StringData::alloc(EvalMemory & mem, size_t size) { - void * t = mem.allocBytes(sizeof(StringData) + size + 1); - if (!t) - throw std::bad_alloc(); + void * t = mem.allocBytes<char, sizeof(StringData) + 1>(size); auto res = new (t) StringData(size); return *res; } @@ -898,7 +896,7 @@ if (context.empty()) return nullptr; - auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size()); + auto ctx = new (mem.allocBytes<value_type, sizeof(Context)>(context.size())) Context(context.size()); std::ranges::transform( context, ctx->elems, [&](const NixStringContextElem & elt) { return &StringData::make(mem, elt.to_string()); }); return ctx; @@ -954,7 +952,7 @@ ListBuilder::ListBuilder(EvalMemory & mem, size_t size) : size(size) - , elems(size <= 2 ? inlineElems : (Value **) mem.allocBytes(size * sizeof(Value *))) + , elems(size <= 2 ? inlineElems : (Value **) mem.allocBytes<Value *>(size)) { } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr/include/nix/expr/eval-inline.hh new/nix-2.35.3/src/libexpr/include/nix/expr/eval-inline.hh --- old/nix-2.35.2/src/libexpr/include/nix/expr/eval-inline.hh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr/include/nix/expr/eval-inline.hh 2026-10-13 13:32:25.000000000 +0200 @@ -26,6 +26,21 @@ return p; } +template<typename T, std::size_t N> +[[gnu::always_inline]] +inline void * EvalMemory::allocBytes(size_t count) +{ + size_t res = 0; + + if (__builtin_mul_overflow(count, sizeof(T), &res)) [[unlikely]] + throw std::bad_alloc(); + + if (__builtin_add_overflow(res, N, &res)) [[unlikely]] + throw std::bad_alloc(); + + return allocBytes(res); +} + [[gnu::always_inline]] Value * EvalMemory::allocValue() { @@ -85,7 +100,7 @@ env = (Env *) p; } else #endif - env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); + env = (Env *) allocBytes<Value *, sizeof(Env)>(size); /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr/include/nix/expr/eval.hh new/nix-2.35.3/src/libexpr/include/nix/expr/eval.hh --- old/nix-2.35.2/src/libexpr/include/nix/expr/eval.hh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr/include/nix/expr/eval.hh 2026-10-13 13:32:25.000000000 +0200 @@ -325,6 +325,14 @@ EvalMemory & operator=(EvalMemory &&) = delete; inline void * allocBytes(size_t n); + + /** + * Allocates @p count * sizeof(T) + N contiguous bytes. + * @throws bad_alloc if the computation would overflow. + */ + template<typename T, std::size_t N = 0> + inline void * allocBytes(size_t count); + inline Value * allocValue(); inline Env & allocEnv(size_t size); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr-c/nix_api_value.cc new/nix-2.35.3/src/libexpr-c/nix_api_value.cc --- old/nix-2.35.2/src/libexpr-c/nix_api_value.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr-c/nix_api_value.cc 2026-10-13 13:32:25.000000000 +0200 @@ -156,7 +156,7 @@ .name = name, .args = {}, .arity = (size_t) arity, - .doc = doc, + .doc = doc ? std::optional<std::string>{doc} : std::nullopt, .impl = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)}; if (args) for (size_t i = 0; args[i]; i++) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr-tests/nix_api_expr.cc new/nix-2.35.3/src/libexpr-tests/nix_api_expr.cc --- old/nix-2.35.2/src/libexpr-tests/nix_api_expr.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr-tests/nix_api_expr.cc 2026-10-13 13:32:25.000000000 +0200 @@ -244,6 +244,16 @@ nix_init_int(context, ret, i * i); } +TEST_F(nix_api_expr_test, nix_alloc_primop_without_doc) +{ + PrimOp * primop = nix_alloc_primop(ctx, primop_square, 1, "undocumented", nullptr, nullptr, nullptr); + assert_ctx_ok(); + ASSERT_NE(nullptr, primop); + + nix_gc_decref(ctx, primop); + assert_ctx_ok(); +} + TEST_F(nix_api_expr_test, nix_expr_primop) { PrimOp * primop = nix_alloc_primop( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libexpr-tests/primops.cc new/nix-2.35.3/src/libexpr-tests/primops.cc --- old/nix-2.35.2/src/libexpr-tests/primops.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libexpr-tests/primops.cc 2026-10-13 13:32:25.000000000 +0200 @@ -1005,4 +1005,18 @@ auto v = eval("builtins.genericClosure { startSet = []; }"); ASSERT_THAT(v, IsListOfSize(0)); } + +TEST_F(PrimOpTest, genListOverflow) +{ + // Test multiplication overflow detection. Shifting out the upper bits would + // leave us with a zero size - that should be caught. + if (sizeof(void *) == 8) { + // (1 << 61) * 8 + ASSERT_THROW({ eval("builtins.genList (x: x) 2305843009213693952"); }, std::bad_alloc); + } else if (sizeof(void *) == 4) { + // (1 << 30) * 4 + ASSERT_THROW({ eval("builtins.genList (x: x) 1073741824"); }, std::bad_alloc); + } +} + } /* namespace nix */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libfetchers/fetch-to-store.cc new/nix-2.35.3/src/libfetchers/fetch-to-store.cc --- old/nix-2.35.2/src/libfetchers/fetch-to-store.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libfetchers/fetch-to-store.cc 2026-10-13 13:32:25.000000000 +0200 @@ -82,6 +82,7 @@ path, store.printStorePath(storePath), hash.to_string(HashFormat::SRI, true)); + settings.srcToStore->cache.insert_or_assign(srcToStoreKey, std::make_tuple(storePath, hash, mode)); return {storePath, hash}; } debug("source path '%s' not in store", path); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libfetchers/git-utils.cc new/nix-2.35.3/src/libfetchers/git-utils.cc --- old/nix-2.35.2/src/libfetchers/git-utils.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libfetchers/git-utils.cc 2026-10-13 13:32:25.000000000 +0200 @@ -23,6 +23,7 @@ #include <git2/describe.h> #include <git2/errors.h> #include <git2/global.h> +#include <git2/index.h> #include <git2/indexer.h> #include <git2/object.h> #include <git2/odb.h> @@ -117,6 +118,7 @@ typedef std::unique_ptr<git_odb, Deleter<git_odb_free>> ObjectDb; typedef std::unique_ptr<git_packbuilder, Deleter<git_packbuilder_free>> PackBuilder; typedef std::unique_ptr<git_indexer, Deleter<git_indexer_free>> Indexer; +typedef std::unique_ptr<git_index, Deleter<git_index_free>> Index; static Hash toHash(const git_oid & oid) { @@ -572,10 +574,27 @@ if (git_status_foreach_ext(*this, &options, &statusCallbackTrampoline, &statusCallback)) throw GitError("getting working directory status"); - /* Get submodule info. */ + /* Gitlinks in the index are the submodules; .gitmodules only + supplies their URLs. */ + std::map<CanonPath, Submodule> configured; auto modulesFile = path / ".gitmodules"; if (pathExists(modulesFile)) - info.submodules = parseSubmodules(modulesFile); + for (auto & submodule : parseSubmodules(modulesFile)) + configured.emplace(submodule.path, submodule); + + Index index; + if (git_repository_index(Setter(index), *this)) + throw GitError("opening index of Git repository %s", PathFmt(path)); + for (size_t n = 0, count = git_index_entrycount(index.get()); n < count; ++n) { + auto entry = git_index_get_byindex(index.get(), n); + if (entry->mode != GIT_FILEMODE_COMMIT || git_index_entry_stage(entry) != 0) + continue; + CanonPath entryPath(entry->path); + if (auto it = configured.find(entryPath); it != configured.end()) + info.submodules.push_back(std::move(it->second)); + else + info.submodules.push_back(Submodule{.path = entryPath}); + } return info; } @@ -968,7 +987,7 @@ auto path2 = *parent; path2.push(entryName); - lookupCache.emplace(path2, std::move(copy)).first->second.get(); + lookupCache.emplace(path2, std::move(copy)); } return res; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libfetchers/git.cc new/nix-2.35.3/src/libfetchers/git.cc --- old/nix-2.35.2/src/libfetchers/git.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libfetchers/git.cc 2026-10-13 13:32:25.000000000 +0200 @@ -767,7 +767,10 @@ { auto head = std::visit( overloaded{ - [&](const std::filesystem::path & path) { return GitRepo::openRepo(path, {})->getWorkdirRef(); }, + [&](const std::filesystem::path & path) -> std::optional<std::string> { + /* A detached HEAD is still resolvable by libgit2. */ + return GitRepo::openRepo(path, {})->getWorkdirRef().value_or("HEAD"); + }, [&](const ParsedURL & url) { return readHeadCached(settings, url.to_string(), shallow); }}, repoInfo.location); if (!head) { @@ -989,10 +992,9 @@ { auto repoPath = repoInfo.getPath().value(); - if (getSubmodulesAttr(input)) - /* Create mountpoints for the submodules. */ - for (auto & submodule : repoInfo.workdirInfo.submodules) - repoInfo.workdirInfo.files.insert(submodule.path); + /* Create mountpoints for the submodules. */ + for (auto & submodule : repoInfo.workdirInfo.submodules) + repoInfo.workdirInfo.files.insert(submodule.path); auto repo = GitRepo::openRepo(repoPath, {}); @@ -1002,12 +1004,19 @@ repo->getAccessor(repoInfo.workdirInfo, {.exportIgnore = exportIgnore}, makeNotAllowedError(repoPath)); /* If the repo has submodules, return a mounted input accessor - consisting of the accessor for the top-level repo and the - accessors for the submodule workdirs. */ - if (getSubmodulesAttr(input) && !repoInfo.workdirInfo.submodules.empty()) { + consisting of the accessor for the top-level repo and, per + submodule, either its workdir accessor or an empty directory + where getAccessorFromCommit() would produce one (submodules + disabled, or a gitlink without a .gitmodules entry). */ + if (!repoInfo.workdirInfo.submodules.empty()) { std::map<CanonPath, nix::ref<SourceAccessor>> mounts; for (auto & submodule : repoInfo.workdirInfo.submodules) { + if (!getSubmodulesAttr(input) || submodule.url.empty()) { + mounts.insert_or_assign(submodule.path, makeEmptySourceAccessor()); + continue; + } + auto submodulePath = repoPath / submodule.path.rel(); fetchers::Attrs attrs; attrs.insert_or_assign("type", "git"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/build/build-log.cc new/nix-2.35.3/src/libstore/build/build-log.cc --- old/nix-2.35.2/src/libstore/build/build-log.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/build/build-log.cc 2026-10-13 13:32:25.000000000 +0200 @@ -10,9 +10,16 @@ void BuildLog::operator()(std::string_view data) { - for (auto c : data) + for (auto c : data) { + /* Only let a '\r' reset the column if it isn't followed by '\n', so + "\r\n" acts as a line terminator; defer a char to handle split chunks. */ + if (pendingCR) { + pendingCR = false; + if (c != '\n') + currentLogLinePos = 0; + } if (c == '\r') - currentLogLinePos = 0; + pendingCR = true; else if (c == '\n') flushLine(); else { @@ -20,6 +27,7 @@ currentLogLine.resize(currentLogLinePos + 1); currentLogLine[currentLogLinePos++] = c; } + } } void BuildLog::flush() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/daemon.cc new/nix-2.35.3/src/libstore/daemon.cc --- old/nix-2.35.2/src/libstore/daemon.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/daemon.cc 2026-10-13 13:32:25.000000000 +0200 @@ -410,6 +410,10 @@ bool repairBool; conn.from >> repairBool; auto repair = RepairFlag{repairBool}; + /* Repairing is not atomic, so disallowed for "untrusted" + clients. */ + if (repair && !trusted) + throw Error("you are not privileged to repair paths"); logger->startWork(); auto pathInfo = [&]() { @@ -491,6 +495,10 @@ case WorkerProto::Op::AddMultipleToStore: { bool repair, dontCheckSigs; conn.from >> repair >> dontCheckSigs; + /* Repairing is not atomic, so disallowed for "untrusted" + clients. */ + if (repair && !trusted) + throw Error("you are not privileged to repair store paths"); if (!trusted && dontCheckSigs) dontCheckSigs = false; @@ -923,6 +931,10 @@ info.sigs = WorkerProto::Serialise<std::set<Signature>>::read(*store, rconn); info.ca = ContentAddress::parseOpt(readString(conn.from)); conn.from >> repair >> dontCheckSigs; + /* Repairing is not atomic, so disallowed for "untrusted" + clients. */ + if (repair && !trusted) + throw Error("you are not privileged to repair store paths"); if (!trusted && dontCheckSigs) dontCheckSigs = false; if (!trusted) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/darwin/build/darwin-derivation-builder.cc new/nix-2.35.3/src/libstore/darwin/build/darwin-derivation-builder.cc --- old/nix-2.35.2/src/libstore/darwin/build/darwin-derivation-builder.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/darwin/build/darwin-derivation-builder.cc 2026-10-13 13:32:25.000000000 +0200 @@ -109,11 +109,6 @@ const size_t breakpoint = sandboxProfile.length() + (1 << 14); for (auto & i : pathsInChroot) { - if (sandboxProfile.length() >= breakpoint) { - debug("Sandbox break: %d %d", sandboxProfile.length(), breakpoint); - sandboxProfile += ")\n(allow file-read* file-write* process-exec\n"; - } - if (i.first != i.second.source) throw Error( "can't map %1% to %2%: mismatched impure paths not supported on Darwin", @@ -127,6 +122,16 @@ continue; throw SysError("getting attributes of required path '%s", path); } + + /* Only open a new allow form once we know this path will actually + * contribute a selector. Opening it before resolving the path + * leaves an unqualified (allow ...) form behind when an optional + * path is missing, and an unqualified allow form render the sandbox useless. */ + if (sandboxProfile.length() >= breakpoint) { + debug("Sandbox break: %d %d", sandboxProfile.length(), breakpoint); + sandboxProfile += ")\n(allow file-read* file-write* process-exec\n"; + } + if (S_ISDIR(optSt->st_mode)) sandboxProfile += fmt("\t(subpath \"%s\")\n", path); else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/derivations.cc new/nix-2.35.3/src/libstore/derivations.cc --- old/nix-2.35.2/src/libstore/derivations.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/derivations.cc 2026-10-13 13:32:25.000000000 +0200 @@ -945,7 +945,8 @@ [&](const DrvHashModulo::DeferredDrv &) { return true; }, // Regular non-CA derivation, replace derivation [&](const DrvHashModulo::DrvHash & drvHash) { - inputs2.insert_or_assign(drvHash.to_string(HashFormat::Base16, false), node); + inputs2[drvHash.to_string(HashFormat::Base16, false)].value.insert( + node.value.begin(), node.value.end()); return false; }, // CA derivation's output hashes @@ -955,11 +956,7 @@ const auto h = get(outputHashes, outputName); if (!h) throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name); - inputs2.insert_or_assign( - h->to_string(HashFormat::Base16, false), - DerivedPathMap<StringSet>::ChildNode{ - .value = {"out"}, - }); + inputs2[h->to_string(HashFormat::Base16, false)].value.insert("out"); } return false; }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/globals.cc new/nix-2.35.3/src/libstore/globals.cc --- old/nix-2.35.2/src/libstore/globals.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/globals.cc 2026-10-13 13:32:25.000000000 +0200 @@ -4,6 +4,7 @@ #include "nix/util/config-global.hh" #include "nix/util/current-process.hh" #include "nix/util/executable-path.hh" +#include "nix/util/file-system.hh" #include "nix/util/args.hh" #include "nix/util/abstract-setting-to-json.hh" #include "nix/util/compute-levels.hh" @@ -254,11 +255,11 @@ // machines. Note that we can’t force processes from executing // x86_64 in aarch64 environments or vice versa since they can // always exec with their own binary preferences. + // + // The runtime file exists iff Rosetta 2 is installed; checking it avoids + // spawning a subprocess during static initialization of `settings`. if (std::string{NIX_LOCAL_SYSTEM} == "aarch64-darwin" - && runProgram( - RunOptions{.program = "arch", .args = {"-arch", "x86_64", "/usr/bin/true"}, .mergeStderrToStdout = true}) - .first - == 0) + && pathExists("/Library/Apple/usr/libexec/oah/libRosettaRuntime")) extraPlatforms.insert("x86_64-darwin"); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/include/nix/store/build/build-log.hh new/nix-2.35.3/src/libstore/include/nix/store/build/build-log.hh --- old/nix-2.35.2/src/libstore/include/nix/store/build/build-log.hh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/include/nix/store/build/build-log.hh 2026-10-13 13:32:25.000000000 +0200 @@ -31,6 +31,8 @@ std::string currentLogLine; size_t currentLogLinePos = 0; // to handle carriage return + bool pendingCR = false; // defer '\r' so "\r\n" is treated as a line terminator + void flushLine(); public: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/local-store.cc new/nix-2.35.3/src/libstore/local-store.cc --- old/nix-2.35.2/src/libstore/local-store.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/local-store.cc 2026-10-13 13:32:25.000000000 +0200 @@ -1065,8 +1065,12 @@ /* The path may have been created by another process in the meantime, so check again. */ if (repair || !isValidPathUncached(info.path)) { - - deletePath(realPath); + /* We'll restore to a temporary location accessible only to us first, confirm that + the narHash/narSize/ca match after restoration and only then move the path into + the expected location. */ + auto [tempDir, tempDirFd] = createTempDirInStore(); + AutoDelete delTempDir(tempDir); + std::filesystem::path tempPath = tempDir / "x"; /* While restoring the path from the NAR, compute the hash of the NAR. */ @@ -1074,7 +1078,7 @@ TeeSource wrapperSource{source, hashSink}; - restorePath(realPath, wrapperSource, config->getLocalSettings().fsyncStorePaths); + restorePath(tempPath, wrapperSource, config->getLocalSettings().fsyncStorePaths); auto hashResult = hashSink.finish(); @@ -1095,7 +1099,7 @@ if (info.ca) { auto & specified = *info.ca; auto actualHash = ({ - SourcePath sourcePath = requireStoreObjectAccessor(info.path, /*requireValidPath=*/false); + SourcePath sourcePath = {makeFSSourceAccessor(tempPath)}; Hash h{HashAlgorithm::SHA256}; // throwaway def to appease C++ auto fim = specified.method.getFileIngestionMethod(); switch (fim) { @@ -1127,6 +1131,19 @@ } } + /* Now that the content address / signed NAR contents is checked, we can move the path into place. + TODO: We should use atomic exchange (RENAME_EXCHANGE and renamex_np with RENAME_SWAP) if possible. */ + + /* Delete the path for good measure even if not repairing. */ + deletePath(realPath); + + /* Move the temporary path we restored above. */ + moveFile(tempPath, realPath); + + /* Clean up the staging directory. */ + delTempDir.deletePath(); + tempDirFd.close(); + autoGC(); canonicalisePathMetaData(realPath, {NIX_WHEN_SUPPORT_ACLS(config->getLocalSettings().ignoredAcls)}); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/s3-binary-cache-store.cc new/nix-2.35.3/src/libstore/s3-binary-cache-store.cc --- old/nix-2.35.2/src/libstore/s3-binary-cache-store.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/s3-binary-cache-store.cc 2026-10-13 13:32:25.000000000 +0200 @@ -376,7 +376,7 @@ req.uri = VerbatimURL(url); req.method = HttpMethod::Delete; - fileTransfer->enqueueFileTransfer(req).get(); + (void) fileTransfer->enqueueFileTransfer(req).get(); } catch (...) { ignoreExceptionInDestructor(); } @@ -409,7 +409,7 @@ req.data = {payload}; req.mimeType = "text/xml"; - fileTransfer->enqueueFileTransfer(req).get(); + (void) fileTransfer->enqueueFileTransfer(req).get(); debug("S3 multipart upload completed: %d parts uploaded for '%s'", partEtags.size(), key); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libstore/unix/build/derivation-builder.cc new/nix-2.35.3/src/libstore/unix/build/derivation-builder.cc --- old/nix-2.35.2/src/libstore/unix/build/derivation-builder.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libstore/unix/build/derivation-builder.cc 2026-10-13 13:32:25.000000000 +0200 @@ -1777,7 +1777,9 @@ useSandbox = params.drv.type().isSandboxed() && !params.drvOptions.noChroot; } - if (store.storeDir != store.config->realStoreDir.get()) { + const bool isRelocatedStore = store.storeDir != store.config->realStoreDir.get(); + + if (isRelocatedStore) { #if defined(__linux__) || defined(__FreeBSD__) useSandbox = true; #else @@ -1790,10 +1792,19 @@ if (!localSettings.sandboxFallback) throw Error( "this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing"); - debug("auto-disabling sandboxing because the prerequisite namespaces are not available"); + + if (isRelocatedStore) + throw Error( + "this system does not support the kernel namespaces that are required for sandboxing, which is required for building in a diverted store"); + + static std::atomic<bool> warned = false; + warnOnce( + warned, + "auto-disabling sandboxing because the prerequisite namespaces are not available and '%1%' is enabled; use '--no-sandbox' or specify 'sandbox = false' setting to silence this warning", + localSettings.sandboxFallback.name); + useSandbox = false; } - #endif if (!useSandbox && params.drvOptions.useUidRange(params.drv)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/libutil/archive.cc new/nix-2.35.3/src/libutil/archive.cc --- old/nix-2.35.2/src/libutil/archive.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/libutil/archive.cc 2026-10-13 13:32:25.000000000 +0200 @@ -1,10 +1,11 @@ +#include <algorithm> #include <cerrno> +#include <limits> #include <map> #include <strings.h> // for strcasecmp #include "nix/util/archive.hh" -#include "nix/util/alignment.hh" #include "nix/util/config-global.hh" #include "nix/util/source-accessor.hh" #include "nix/util/source-path.hh" @@ -151,7 +152,7 @@ sink.preallocateContents(size); if (sink.skipContents) { - uint64_t left = alignUp(size, 8); + uint64_t left = size; /* Source::skip takes a size_t, which might be narrower on 32 bit systems, so be careful around truncations. */ while (left) { @@ -159,11 +160,10 @@ source.skip(toSkip); left -= toSkip; } - return; + } else { + source.drainInto(sink, size); } - source.drainInto(sink, size); - readPadding(size, source); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/src/nix/flake-prefetch-inputs.cc new/nix-2.35.3/src/nix/flake-prefetch-inputs.cc --- old/nix-2.35.2/src/nix/flake-prefetch-inputs.cc 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/src/nix/flake-prefetch-inputs.cc 2026-10-13 13:32:25.000000000 +0200 @@ -42,7 +42,9 @@ if (!state_.lock()->done.insert(&node).second) return; - if (auto lockedNode = dynamic_cast<const LockedNode *>(&node)) { + // Relative path inputs are part of their parent's source tree. + if (auto lockedNode = dynamic_cast<const LockedNode *>(&node); + lockedNode && !lockedNode->lockedRef.input.isRelative()) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("fetching '%s'", lockedNode->lockedRef)); auto accessor = lockedNode->lockedRef.input.getAccessor(fetchSettings, *store).first; @@ -56,11 +58,11 @@ for (auto & [inputName, input] : node.inputs) { if (auto inputNode = std::get_if<0>(&input)) - pool.enqueue(std::bind(visit, **inputNode)); + pool.enqueue([&visit, inputNode(*inputNode)] { visit(*inputNode); }); } }; - pool.enqueue(std::bind(visit, *flake.lockFile.root)); + pool.enqueue([&] { visit(*flake.lockFile.root); }); pool.process(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/tests/functional/fetchGit.sh new/nix-2.35.3/tests/functional/fetchGit.sh --- old/nix-2.35.2/tests/functional/fetchGit.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/tests/functional/fetchGit.sh 2026-10-13 13:32:25.000000000 +0200 @@ -211,6 +211,9 @@ git -C "$TEST_ROOT"/minimal fetch "$repo" "$rev2" git -C "$TEST_ROOT"/minimal checkout "$rev2" [[ $(nix eval --impure --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/minimal; }).rev") = "$rev2" ]] +# Detached HEAD must not fall back to a guessed 'master' ref. +expectStderr 0 nix eval --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/minimal; rev = \"$rev2\"; }).outPath" \ + | grepQuietInverse "could not read HEAD ref" # Explicit ref = "HEAD" should work, and produce the same outPath as without ref path7=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).outPath") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/tests/functional/fetchGitSubmodules.sh new/nix-2.35.3/tests/functional/fetchGitSubmodules.sh --- old/nix-2.35.2/tests/functional/fetchGitSubmodules.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/tests/functional/fetchGitSubmodules.sh 2026-10-13 13:32:25.000000000 +0200 @@ -36,7 +36,21 @@ rev=$(git -C "$rootRepo" rev-parse HEAD) +# A clean workdir must yield the same tree as fetching its HEAD rev: the +# submodule is an empty directory, whether or not it is checked out. +r0=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; }).outPath") +[[ -d $r0/sub ]] +[[ -z "$(ls -A "$r0/sub")" ]] +# Both paths share a fingerprint, so compare narHashes with separate caches. +hashWorkdir=$(XDG_CACHE_HOME=$TEST_ROOT/cache-workdir nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; }).narHash") +hashRev=$(XDG_CACHE_HOME=$TEST_ROOT/cache-rev nix eval --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; rev = \"$rev\"; }).narHash") +[[ $hashWorkdir == "$hashRev" ]] +# The non-local (clone-to-cache) code path must agree as well. +hashHttp=$(_NIX_FORCE_HTTP=1 XDG_CACHE_HOME=$TEST_ROOT/cache-http nix eval --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; rev = \"$rev\"; }).narHash") +[[ $hashWorkdir == "$hashHttp" ]] + r1=$(nix eval --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; rev = \"$rev\"; }).outPath") +[[ $r0 == "$r1" ]] r2=$(nix eval --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; rev = \"$rev\"; submodules = false; }).outPath") r3=$(nix eval --raw --expr "(builtins.fetchGit { url = \"file://$rootRepo\"; rev = \"$rev\"; submodules = true; }).outPath") @@ -226,3 +240,28 @@ } test_submodule_nested + + +# A gitlink without a .gitmodules entry is still a submodule (#15423): it must +# render the same (empty directory) from the workdir and by rev. +test_gitlink_without_gitmodules() { + local repo=$TEST_ROOT/nogitmodules + createGitRepo "$repo" + git -C "$repo" submodule add "$subRepo" sub + git -C "$repo" commit -m "Add submodule" + git -C "$repo" rm .gitmodules + git -C "$repo" commit -m "Remove .gitmodules" + local rev + rev=$(git -C "$repo" rev-parse HEAD) + + for submodules in true false; do + local hashWorkdir hashRev outWorkdir + hashWorkdir=$(XDG_CACHE_HOME=$TEST_ROOT/ngm-workdir-$submodules nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; submodules = $submodules; }).narHash") + hashRev=$(XDG_CACHE_HOME=$TEST_ROOT/ngm-rev-$submodules nix eval --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; rev = \"$rev\"; submodules = $submodules; }).narHash") + [[ $hashWorkdir == "$hashRev" ]] + outWorkdir=$(XDG_CACHE_HOME=$TEST_ROOT/ngm-workdir-$submodules nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; submodules = $submodules; }).outPath") + [[ -d $outWorkdir/sub ]] + [[ -z "$(ls -A "$outWorkdir/sub")" ]] + done +} +test_gitlink_without_gitmodules diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/tests/functional/flakes/relative-paths.sh new/nix-2.35.3/tests/functional/flakes/relative-paths.sh --- old/nix-2.35.2/tests/functional/flakes/relative-paths.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/tests/functional/flakes/relative-paths.sh 2026-10-13 13:32:25.000000000 +0200 @@ -68,6 +68,9 @@ git -C "$rootFlake" add sub2/flake.lock [[ $(nix eval "$subflake2#y") = 15 ]] +# Relative path inputs must not make prefetch-inputs fail. +nix flake prefetch-inputs "$subflake2" + [[ $(jq --indent 0 --compact-output . < "$subflake2/flake.lock") =~ ^'{"nodes":{"root":{"inputs":{"root":"root_2","sub1":"sub1"}},"root_2":{"inputs":{"sub0":"sub0"},"locked":{"path":"..","type":"path"},"original":{"path":"..","type":"path"},"parent":[]},"root_3":{"inputs":{"sub0":"sub0_2"},"locked":{"path":"../","type":"path"},"original":{"path":"../","type":"path"},"parent":["sub1"]},"sub0":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["root"]},"sub0_2":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["sub1","root"]},"sub1":{"inputs":{"root":"root_3"},"locked":{"path":"../sub1","type":"path"},"original":{"path":"../sub1","type":"path"},"parent":[]}},"root":"root","version":7}'$ ]] # Make sure there are no content locks for relative path flakes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/tests/functional/gc-closure.sh new/nix-2.35.3/tests/functional/gc-closure.sh --- old/nix-2.35.2/tests/functional/gc-closure.sh 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/tests/functional/gc-closure.sh 2026-10-13 13:32:25.000000000 +0200 @@ -12,7 +12,7 @@ input0=$(realpath "$TEST_ROOT/gc-root") input1=$(nix build -f dependencies2.nix input1_drv --no-link --print-out-paths) input2=$(nix build -f dependencies2.nix input2_drv --no-link --print-out-paths) - input2_out=$(printf "%s" "$input2" | head -n1) + input2_out=$(head -n1 <<< "$input2") input2_out2=$(printf "%s" "$input2" | tail -n1) top=$(nix build -f dependencies2.nix --no-link --print-out-paths) something_else=$(nix store add-path ./dependencies2.nix) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.35.2/tests/installer/default.nix new/nix-2.35.3/tests/installer/default.nix --- old/nix-2.35.2/tests/installer/default.nix 2026-08-12 23:09:18.000000000 +0200 +++ new/nix-2.35.3/tests/installer/default.nix 2026-10-13 13:32:25.000000000 +0200 @@ -4,10 +4,31 @@ }: let + commonCheck = '' + export NIX_CONFIG="substituters = " - installScripts = { + nix-env --version + + nix --extra-experimental-features nix-command store info + nix-store --verify --check-contents + nix store verify --all --extra-experimental-features nix-command --no-trust + + nix-channel --add file://$HOME/channel myChannel + nix-channel --update + [[ $(nix-instantiate --eval --expr 'builtins.readFile <myChannel/someFile>') = '"someContent"' ]] + + bad_mtime=$( find /nix/store/ -mindepth 1 ! -path /nix/store/.links \ + -exec sh -c '[ "$(stat -c %Y "{}")" -ne 1 ]' \; -print -quit ) + if [ -n "$bad_mtime" ]; then + echo "bad filesystem object mtime after install:" + stat "$bad_mtime" + exit 1 + fi + ''; + + installCases = { install-default = { - script = '' + install = '' tar -xf ./nix.tar.xz mv ./nix-* nix ./nix/install --no-channel-add @@ -15,7 +36,7 @@ }; install-both-profile-links = { - script = '' + install = '' tar -xf ./nix.tar.xz mv ./nix-* nix ln -s $HOME/.local/state/nix/profiles/a-profile $HOME/.nix-profile @@ -26,7 +47,7 @@ }; install-force-no-daemon = { - script = '' + install = '' tar -xf ./nix.tar.xz mv ./nix-* nix ./nix/install --no-daemon --no-channel-add @@ -34,11 +55,26 @@ }; install-force-daemon = { - script = '' + install = '' tar -xf ./nix.tar.xz mv ./nix-* nix ./nix/install --daemon --no-channel-add ''; + + check = '' + [ "$(stat -c '%a %U:%G' /nix/store)" = "1775 root:nixbld" ] || { + echo "/nix/store is not 1775 root:nixbld:" + stat /nix/store + exit 1 + } + + bad_ownership=$(find /nix/store/ -mindepth 1 ! \( -user root -group root \) -print -quit) + if [ -n "$bad_ownership" ]; then + echo "bad store object ownership in the store after install:" + stat "$bad_ownership" + exit 1 + fi + ''; }; }; @@ -136,13 +172,13 @@ postBoot = disableSELinux; extraQemuOpts = "-cpu Westmere-v2"; }; - }; makeTest = imageName: testName: let image = images.${imageName}; + test = installCases.${testName}; in with nixpkgsFor.${image.system}.native; runCommand "installer-test-${imageName}-${testName}" @@ -153,11 +189,13 @@ ]; image = image.image; postBoot = image.postBoot or ""; - installScript = installScripts.${testName}.script; + installScript = test.install; + checkScript = commonCheck + (test.check or ""); binaryTarball = binaryTarballs.${image.system}; } '' shopt -s nullglob + set -eu echo "Unpacking Vagrant box $image..." tar xvf $image @@ -207,7 +245,7 @@ if [[ -n $postBoot ]]; then echo "Running post-boot commands..." - $ssh "set -ex; $postBoot" + $ssh "set -eux; $postBoot" fi echo "Copying installer..." @@ -223,30 +261,14 @@ echo "Testing Nix installation..." $ssh <<EOF - set -ex - # FIXME: get rid of this; ideally ssh should just work. source ~/.bash_profile || true source ~/.bash_login || true source ~/.profile || true source /etc/bashrc || true - nix-env --version - nix --extra-experimental-features nix-command store info - - out=\$(nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > \$out"]; }') - [[ \$(cat \$out) = foobar ]] - - if pgrep nix-daemon; then - MAYBESUDO="sudo" - else - MAYBESUDO="" - fi - - - $MAYBESUDO \$(which nix-channel) --add file://\$HOME/channel myChannel - $MAYBESUDO \$(which nix-channel) --update - [[ \$(nix-instantiate --eval --expr 'builtins.readFile <myChannel/someFile>') = '"someContent"' ]] + set -eux + $checkScript EOF echo "Done!" @@ -256,5 +278,5 @@ in builtins.mapAttrs (imageName: image: { - ${image.system} = builtins.mapAttrs (testName: test: makeTest imageName testName) installScripts; + ${image.system} = builtins.mapAttrs (testName: test: makeTest imageName testName) installCases; }) images
