tests/functional2: migrate tarball.sh
Change-Id: I8fb889a5723e34453597fbce2e027fb49a859319
This commit is contained in:
@@ -72,7 +72,6 @@ functional_tests_scripts = [
|
||||
'export-graph.sh',
|
||||
'fetchGitRefs.sh',
|
||||
'gc-runtime.sh',
|
||||
'tarball.sh',
|
||||
'fetchers.sh',
|
||||
'fetchGit.sh',
|
||||
'fetchGitLocked.sh',
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
rm -rf $TEST_HOME
|
||||
|
||||
tarroot=$TEST_ROOT/tarball
|
||||
rm -rf $tarroot
|
||||
mkdir -p $tarroot
|
||||
cp dependencies.nix $tarroot/default.nix
|
||||
cp config.nix dependencies.builder*.sh $tarroot/
|
||||
touch -d '@1000000000' $tarroot $tarroot/*
|
||||
|
||||
hash=$(nix hash path $tarroot)
|
||||
|
||||
test_tarball() {
|
||||
local ext="$1"
|
||||
local compressor="$2"
|
||||
|
||||
tarball=$TEST_ROOT/tarball.tar$ext
|
||||
(cd $TEST_ROOT && GNUTAR_REPRODUCIBLE= tar --mtime=$tarroot/default.nix --owner=0 --group=0 --numeric-owner --sort=name -c -f - tarball) | $compressor > $tarball
|
||||
|
||||
nix-env -f file://$tarball -qa --out-path | grepQuiet dependencies
|
||||
|
||||
nix-build -o $TEST_ROOT/result file://$tarball
|
||||
|
||||
nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball
|
||||
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTarball \"file://$tarball\")"
|
||||
# Do not re-fetch paths already present
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTarball { url = \"file:///does-not-exist/must-remain-unused/$tarball\"; sha256 = \"$hash\"; })"
|
||||
|
||||
# Regression test: Ensure tarballs can be unpacked when the
|
||||
# temporary directory is a symbolic link.
|
||||
(
|
||||
export HOME=$(mktemp -d)
|
||||
ln -sf "$TMPDIR" "$HOME/tmp"
|
||||
nix-build -o "$TEST_ROOT/result" --temp-dir "$HOME/tmp" -E "import (fetchTarball \"file://$tarball\")"
|
||||
)
|
||||
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTree \"file://$tarball\")"
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = \"file://$tarball\"; })"
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = \"file://$tarball\"; narHash = \"$hash\"; })"
|
||||
# Do not re-fetch paths already present
|
||||
nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = \"file:///does-not-exist/must-remain-unused/$tarball\"; narHash = \"$hash\"; })"
|
||||
expectStderr 102 nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = \"file://$tarball\"; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" | grep 'NAR hash mismatch in input'
|
||||
|
||||
[[ $(nix eval --impure --expr "(fetchTree \"file://$tarball\").lastModified") = 1000000000 ]]
|
||||
|
||||
nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = \"file://$tarball\"; narHash = \"$hash\"; })) ? submodules)" >&2
|
||||
nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = \"file://$tarball\"; narHash = \"$hash\"; })) ? submodules)" 2>&1 | grep 'true'
|
||||
|
||||
nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar$ext
|
||||
nix-instantiate --eval -E 'with <fnord/xyzzy>; 1 + 2' -I fnord=file://no-such-tarball$ext
|
||||
(! nix-instantiate --eval -E '<fnord/xyzzy> 1' -I fnord=file://no-such-tarball$ext)
|
||||
|
||||
nix-instantiate --eval -E '<fnord/config.nix>' -I fnord=file://no-such-tarball$ext -I fnord=.
|
||||
|
||||
# Ensure that the `name` attribute isn’t accepted as that would mess
|
||||
# with the content-addressing
|
||||
(! nix-instantiate --eval -E "fetchTree { type = \"tarball\"; url = \"file://$tarball\"; narHash = \"$hash\"; name = \"foo\"; }")
|
||||
|
||||
}
|
||||
|
||||
test_tarball '' cat
|
||||
test_tarball .xz xz
|
||||
test_tarball .gz gzip
|
||||
|
||||
rm -rf $TEST_ROOT/tmp
|
||||
mkdir -p $TEST_ROOT/tmp
|
||||
(! TMPDIR=$TEST_ROOT/tmp XDG_RUNTIME_DIR=$TEST_ROOT/tmp nix-env -f file://$(pwd)/bad.tar.xz -qa --out-path)
|
||||
(! [ -e $TEST_ROOT/tmp/bad ])
|
||||
@@ -0,0 +1,224 @@
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
|
||||
from functional2.testlib.fixtures.command import Command
|
||||
from functional2.testlib.fixtures.file_helper import with_files, Symlink, CopyFile
|
||||
from functional2.testlib.fixtures.nix import Nix
|
||||
from functional2.testlib.utils import get_global_asset_pack, get_global_asset
|
||||
|
||||
|
||||
TAR_FILES = {
|
||||
"tarball": get_global_asset_pack("dependencies")
|
||||
| {"default.nix": get_global_asset("dependencies/dependencies.nix")}
|
||||
}
|
||||
|
||||
|
||||
def _set_mtime_of_folder(path: Path, mtime: int = 1000000000):
|
||||
for file in [path] + list(path.iterdir()) if path.is_dir() else []:
|
||||
os.utime(file, (mtime, mtime))
|
||||
|
||||
|
||||
@pytest.fixture(params=[("", "cat"), (".xz", "xz"), (".gz", "gzip")])
|
||||
def tarball(request: FixtureRequest, nix: Nix, files: Path) -> Path:
|
||||
ext, compressor = request.param
|
||||
# setup
|
||||
_set_mtime_of_folder(files / "tarball")
|
||||
env = nix.env
|
||||
env["GNUTAR_REPRODUCIBLE"] = ""
|
||||
tar_exe = shutil.which("tar")
|
||||
env.path.add_program("tar")
|
||||
tarball_name = f"tarball.tar{ext}"
|
||||
tarball_path = files / tarball_name
|
||||
|
||||
# Create tarball
|
||||
tarball_content = (
|
||||
Command(
|
||||
[
|
||||
"tar",
|
||||
f"--mtime={files / 'tarball' / 'default.nix'}",
|
||||
"--owner=0",
|
||||
"--group=0",
|
||||
"--numeric-owner",
|
||||
"--sort=name",
|
||||
f"--to-command={compressor}",
|
||||
"-c",
|
||||
"-f",
|
||||
"-",
|
||||
"tarball",
|
||||
],
|
||||
env,
|
||||
exe=tar_exe,
|
||||
)
|
||||
.run()
|
||||
.ok()
|
||||
.stdout
|
||||
)
|
||||
tarball_path.write_bytes(tarball_content)
|
||||
|
||||
res = nix.nix_env(["-f", f"file://{tarball_path}", "-qa", "--out-path"], build=True).run().ok()
|
||||
assert "dependencies" in res.stdout_plain
|
||||
|
||||
return tarball_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tar_hash(files: Path, nix: Nix) -> str:
|
||||
return nix.hash_path(files / "tarball")
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
@pytest.mark.parametrize(
|
||||
"flags",
|
||||
[
|
||||
["file://{tarball}"], # noqa: RUF027 # yes, true, but we don't have the variables here
|
||||
["<foo>", "-I", "foo=file://{tarball}"], # noqa: RUF027
|
||||
["-E", 'import (fetchTarball "file://{tarball}")'], # noqa: RUF027
|
||||
# Do not re-fetch paths already present
|
||||
[
|
||||
"-E",
|
||||
'import (fetchTarball {{ url = "file:///does-not-exist/must-remain-unused/{tarball}"; sha256 = "{tar_hash}"; }})', # noqa: RUF027
|
||||
],
|
||||
],
|
||||
)
|
||||
def test_fetch_tarball(nix: Nix, tarball: Path, tar_hash: str, flags: list[str]):
|
||||
# HACK(Commentator2.0): if we don't create a copy, we'd try to format it twice, as it is the same list used in other test calls
|
||||
flags = flags[:]
|
||||
|
||||
flags[-1] = flags[-1].format(tarball=tarball, tar_hash=tar_hash)
|
||||
nix.nix_build(["-o", "result", *flags]).run().ok()
|
||||
|
||||
|
||||
@with_files(TAR_FILES | {"actual-tmp-dir": {}, "tmp-dir": Symlink("./actual-tmp-dir")})
|
||||
def test_tarball_symlink_extraction(nix: Nix, files: Path, tarball: Path):
|
||||
nix.env.dirs.tmpdir = files / "tmp-dir"
|
||||
|
||||
nix.nix_build(
|
||||
["-o", "result", "-E", f'import (fetchTarball "file://{files / tarball.name}")']
|
||||
).run().ok()
|
||||
|
||||
real_tmp_dir = nix.env.dirs.test_root / "tmp"
|
||||
real_tmp_dir.mkdir(exist_ok=True)
|
||||
nix.env.dirs.tmpdir = real_tmp_dir
|
||||
|
||||
nix.nix_build(
|
||||
[
|
||||
"-o",
|
||||
"result",
|
||||
"--temp-dir",
|
||||
f"{files}/tmp-dir",
|
||||
"-E",
|
||||
f'import (fetchTarball "file://{files / tarball.name}")',
|
||||
]
|
||||
).run().ok()
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
@pytest.mark.parametrize(
|
||||
"expr",
|
||||
[
|
||||
'import (fetchTree "file://{tarball}")', # noqa: RUF027 # yes, true, but we don't have the variables here
|
||||
'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; }})', # noqa: RUF027
|
||||
'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; }})', # noqa: RUF027
|
||||
],
|
||||
)
|
||||
def test_fetch_tree(nix: Nix, tarball: Path, tar_hash: str, expr: str):
|
||||
expr = expr.format(tarball=tarball, tar_hash=tar_hash)
|
||||
nix.nix_build(["-o", "result", "-E", expr], flake=True).run().ok()
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
def test_fetch_tree_hash_mismatch(nix: Nix, tarball: Path):
|
||||
res = (
|
||||
nix.nix_build(
|
||||
[
|
||||
"-o",
|
||||
"result",
|
||||
"-E",
|
||||
f'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc="; }})',
|
||||
],
|
||||
flake=True,
|
||||
)
|
||||
.run()
|
||||
.expect(102)
|
||||
)
|
||||
assert "NAR hash mismatch in input" in res.stderr_plain
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
def test_last_modified(nix: Nix, tarball: Path):
|
||||
res = (
|
||||
nix.nix(
|
||||
["eval", "--impure", "--expr", f'(fetchTree "file://{tarball}").lastModified'],
|
||||
flake=True,
|
||||
build=True,
|
||||
)
|
||||
.run()
|
||||
.ok()
|
||||
)
|
||||
assert res.stdout_plain == "1000000000"
|
||||
|
||||
|
||||
@with_files({"config.nix": get_global_asset("config.nix")})
|
||||
@pytest.mark.parametrize(
|
||||
("flags", "exit_code"),
|
||||
[
|
||||
(["1 + 2"], 0),
|
||||
(["with <fnord/xyzzy>; 1 + 2"], 0),
|
||||
(["<fnord/config.nix>", "-I", "fnord=."], 0),
|
||||
(["<fnord/xyzzy> 1"], 1),
|
||||
],
|
||||
)
|
||||
def test_no_accessing_tar(nix: Nix, flags: list[str], exit_code: int):
|
||||
nix.nix_instantiate(
|
||||
["--eval", "-I", "fnord=file://no-such-tarball.tar.gz", "-E", *flags]
|
||||
).run().expect(exit_code)
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
def test_no_submodules(nix: Nix, tarball: Path, tar_hash: str):
|
||||
res = (
|
||||
nix.nix_instantiate(
|
||||
[
|
||||
"--strict",
|
||||
"--eval",
|
||||
"-E",
|
||||
f'!((fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; }})) ? submodules',
|
||||
],
|
||||
flake=True,
|
||||
build=True,
|
||||
)
|
||||
.run()
|
||||
.ok()
|
||||
)
|
||||
assert res.stdout_plain == "true"
|
||||
|
||||
|
||||
@with_files(TAR_FILES)
|
||||
def test_no_accessing_name(nix: Nix, tarball: Path, tar_hash: str):
|
||||
"""
|
||||
Ensure that the `name` attribute isn't accepted as that would mess with the content-addressing
|
||||
"""
|
||||
res = (
|
||||
nix.nix_instantiate(
|
||||
[
|
||||
"--eval",
|
||||
"-E",
|
||||
f'fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; name = "foo"; }}',
|
||||
],
|
||||
flake=True,
|
||||
)
|
||||
.run()
|
||||
.expect(1)
|
||||
)
|
||||
assert "error: attribute 'name' isn’t supported in call" in res.stderr_plain # noqa: RUF001 # for some reason, this error message wants to feel special
|
||||
|
||||
|
||||
@with_files({"bad.tar.xz": CopyFile("assets/test_tarball/bad.tar.xz")})
|
||||
def test_nix_env_bad_tarball(nix: Nix, files: Path):
|
||||
res = nix.nix_env(["-f", f"file://{files / 'bad.tar.xz'}", "-qa", "--out-path"]).run().expect(1)
|
||||
assert "error: failed to extract archive (Path contains '..')" in res.stderr_plain
|
||||
assert not (nix.env.dirs.tmpdir / "bad").exists()
|
||||
@@ -139,6 +139,7 @@ class _Dirs:
|
||||
nix_store_dir: Path | None
|
||||
cache_dir: Path | None
|
||||
xdg_cache_home: Path | None
|
||||
tmpdir: Path | None
|
||||
"""used for nar caching"""
|
||||
|
||||
def get_env_keys(self) -> set[str]:
|
||||
@@ -176,6 +177,7 @@ class ManagedEnv:
|
||||
nix_store_dir=self._get_dir("nix/store"),
|
||||
cache_dir=self._get_dir("binary-cache"),
|
||||
xdg_cache_home=self._get_dir("test-home/.cache"),
|
||||
tmpdir=self._get_dir("tmp"),
|
||||
)
|
||||
self.path.prepend(self.dirs.nix_bin_dir)
|
||||
self.init_defaults(global_path)
|
||||
|
||||
@@ -128,6 +128,7 @@ def test_env_to_env(tmp_path: Path):
|
||||
"XDG_CACHE_HOME",
|
||||
"PATH",
|
||||
"BUILD_TEST_SHELL",
|
||||
"TMPDIR",
|
||||
} | ({"_NIX_TEST_NO_SANDBOX"} if sys.platform == "darwin" else set())
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user