nix3 CLI never honored that setting. When we update a nix3 profile element with new built store paths for which we know the deriver (the derivation path which built it), we add the derivation paths to the profile element to prevent garbage collection the same way the nix2 CLI logic does. Fixes #1095. Change-Id: Icc236d174c5ce5ffe042d1c8e1968f6cb5b50359 Signed-off-by: Raito Bezarius <raito@lix.systems>
319 lines
13 KiB
Python
319 lines
13 KiB
Python
import re
|
|
import json
|
|
import pytest
|
|
from textwrap import dedent
|
|
from pathlib import Path
|
|
|
|
from testlib.fixtures.file_helper import File, with_files
|
|
from testlib.fixtures.nix import Nix
|
|
from testlib.utils import get_global_asset, get_global_asset_pack
|
|
from testlib.fixtures.env import ManagedEnv
|
|
from testlib.fixtures.command import Command
|
|
from testlib.environ import environ
|
|
|
|
system = environ.get("system")
|
|
|
|
flake = {
|
|
"flake.nix": File("""
|
|
{
|
|
description = "Bla bla";
|
|
|
|
outputs = { self }: with import ./config.nix; rec {
|
|
packages.${system}.default = mkDerivation {
|
|
name = "profile-test-${builtins.readFile ./version}";
|
|
outputs = [ "out" "man" "dev" ];
|
|
builder = builtins.toFile "builder.sh"
|
|
''
|
|
mkdir -p $out/bin
|
|
cat > $out/bin/hello <<EOF
|
|
#! ${shell}
|
|
echo Hello ${builtins.readFile ./who}
|
|
EOF
|
|
chmod +x $out/bin/hello
|
|
echo DONE
|
|
mkdir -p $man/share/man
|
|
mkdir -p $dev/include
|
|
'';
|
|
__contentAddressed = import ./ca.nix;
|
|
outputHashMode = "recursive";
|
|
outputHashAlgo = "sha256";
|
|
meta.outputsToInstall = [ "out" "man" ];
|
|
};
|
|
};
|
|
}
|
|
"""),
|
|
"who": File("World"),
|
|
"version": File("1.0"),
|
|
"ca.nix": File("false"),
|
|
"config.nix": get_global_asset("config.nix"),
|
|
}
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def configure_system(nix: Nix):
|
|
nix.settings.add_xp_feature("nix-command", "flakes")
|
|
|
|
|
|
def run_hello(env: ManagedEnv) -> str:
|
|
return Command([str(env.dirs.home / ".nix-profile/bin/hello")], _env=env).run().ok().stdout_s
|
|
|
|
|
|
def build(nix: Nix, thing: str) -> str:
|
|
return nix.nix(["build", "--no-link", "--print-out-paths", thing]).run().ok().stdout_s.strip()
|
|
|
|
|
|
class TestUpgradeProfile:
|
|
@with_files({"flake1": flake} | get_global_asset_pack("user-envs"))
|
|
def test_nix_env_upgrade(self, nix: Nix, env: ManagedEnv):
|
|
nix.nix_env(["-f", "./user-envs.nix", "-i", "foo-1.0"]).run().ok()
|
|
result = nix.nix(["profile", "list"]).run().ok().stdout_s
|
|
assert re.match(r"Name:.*foo\nStore paths:.*foo-1.0", result)
|
|
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
result = nix.nix(["profile", "list"]).run().ok().stdout_s
|
|
assert re.search(r"Name:.*flake1\n.*?\n.*?\nLocked flake URL:.*narHash", result)
|
|
|
|
assert run_hello(env) == "Hello World\n"
|
|
assert (env.dirs.home / ".nix-profile/share/man").exists()
|
|
assert not (env.dirs.home / ".nix-profile/include").exists()
|
|
|
|
result = nix.nix(["profile", "history"]).run().ok().stdout_s
|
|
assert f"packages.{system}.default: ∅ -> 1.0" in result
|
|
|
|
result = nix.nix(["profile", "diff-closures"]).run().ok().stdout_s
|
|
assert "env-manifest.nix: ε → ∅" in result
|
|
|
|
@with_files({"flake1": flake})
|
|
def test_upgrade_from_v2(self, nix: Nix, env: ManagedEnv):
|
|
import_profile = env.dirs.home / "import-profile"
|
|
import_profile.mkdir()
|
|
|
|
path = build(nix, "./flake1^out")
|
|
(import_profile / "manifest.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"version": 2,
|
|
"elements": [
|
|
{
|
|
"active": True,
|
|
"attrPath": "legacyPackages.x86_64-linux.hello",
|
|
"originalUrl": "flake:nixpkgs",
|
|
"outputs": None,
|
|
"priority": 5,
|
|
"storePaths": [path],
|
|
"url": "github:NixOS/nixpkgs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
}
|
|
],
|
|
}
|
|
)
|
|
)
|
|
|
|
added_path = nix.nix(["store", "add-path", str(import_profile)]).run().ok().stdout_s.strip()
|
|
nix.nix(
|
|
["build", "--profile", str(env.dirs.home / ".nix-profile"), str(added_path)]
|
|
).run().ok()
|
|
|
|
result = nix.nix(["profile", "list"]).run().ok().stdout_s
|
|
assert re.match("Name:.*hello\n.*\nStore paths:.*" + path, result, re.S)
|
|
assert (
|
|
"removed 1 packages, kept 0 packages"
|
|
in nix.nix(["profile", "remove", "hello"]).run().ok().stderr_s
|
|
)
|
|
|
|
|
|
class ProfileTestBase:
|
|
@pytest.fixture(autouse=True)
|
|
def setup(self, nix: Nix, configure_system, files): # noqa: ANN001, ARG002
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
|
|
|
|
class TestBasicOperations(ProfileTestBase):
|
|
@with_files({"flake1": flake})
|
|
def test_xdg_base_dir_support(self, nix: Nix, env: ManagedEnv):
|
|
nix.settings.use_xdg_base_directories = True
|
|
|
|
result = nix.nix(["profile", "remove", "flake1", "-L"]).run().ok()
|
|
assert "removed 1 packages" in result.stderr_s
|
|
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
|
|
@with_files({"flake1": flake})
|
|
def test_wipe_history(self, nix: Nix):
|
|
nix.nix(["profile", "wipe-history"]).run().ok()
|
|
history = nix.nix(["profile", "history"]).run().ok().stdout_s
|
|
assert len(re.findall(r"^Version ", history, re.M)) == 1
|
|
|
|
@with_files({"flake1": flake} | get_global_asset_pack("simple-drv"))
|
|
def test_install_non_flake(self, nix: Nix, env: ManagedEnv):
|
|
nix.nix(["profile", "install", "--file", "./simple.nix", ""]).run().ok()
|
|
assert (env.dirs.home / ".nix-profile/hello").read_text() == "Hello World!\n"
|
|
assert "removed 1 packages" in nix.nix(["profile", "remove", "simple"]).run().ok().stderr_s
|
|
|
|
assert not (env.dirs.home / ".nix-profile/hello").exists()
|
|
output = nix.nix_build(["--no-out-link", "./simple.nix"]).run().ok().stdout_s.strip()
|
|
nix.nix(["profile", "install", output]).run().ok()
|
|
assert (env.dirs.home / ".nix-profile/hello").read_text() == "Hello World!\n"
|
|
|
|
@with_files(
|
|
{
|
|
"flake1": flake,
|
|
"simple1": get_global_asset_pack("simple-drv"),
|
|
"simple2": get_global_asset_pack("simple-drv"),
|
|
}
|
|
)
|
|
def test_install_different_sources(self, nix: Nix):
|
|
nix.nix(["profile", "install", "--file", "./simple1/simple.nix", ""]).run().ok()
|
|
nix.nix(["profile", "install", "--file", "./simple2/simple.nix", ""]).run().ok()
|
|
|
|
result = nix.nix(["profile", "list"]).run().ok().stdout_s
|
|
assert re.search(r"Name:.*simple", result)
|
|
assert re.search(r"Name:.*simple-1", result)
|
|
|
|
assert "removed 1 packages" in nix.nix(["profile", "remove", "simple"]).run().ok().stderr_s
|
|
assert (
|
|
"removed 1 packages" in nix.nix(["profile", "remove", "simple-1"]).run().ok().stderr_s
|
|
)
|
|
|
|
@with_files({"flake1": flake, "flake2": flake | {"who": File("World2")}})
|
|
def test_priority(self, nix: Nix, env: ManagedEnv):
|
|
hello1 = build(nix, "./flake1#default.out")
|
|
hello2 = build(nix, "./flake2#default.out")
|
|
|
|
result = nix.nix(["--offline", "profile", "install", "./flake2"]).run().expect(1)
|
|
result = [
|
|
line
|
|
for line in result.stderr_s.splitlines()
|
|
if not line.startswith("warning: ")
|
|
and not line.startswith("error (ignored): ")
|
|
and not re.match(r"^fetching .+ input", line)
|
|
and line
|
|
]
|
|
assert result == [
|
|
"error: An existing package already provides the following file:",
|
|
f" {hello1}/bin/hello",
|
|
" This is the conflicting file from the new package:",
|
|
f" {hello2}/bin/hello",
|
|
" To remove the existing package:",
|
|
f" nix profile remove path:{env.dirs.home}/flake1#packages.{system}.default",
|
|
" The new package can also be installed next to the existing one by assigning a different priority.",
|
|
" The conflicting packages have a priority of 5.",
|
|
" To prioritise the new package:",
|
|
f" nix profile install path:{env.dirs.home}/flake2#packages.{system}.default --priority 4",
|
|
" To prioritise the existing package:",
|
|
f" nix profile install path:{env.dirs.home}/flake2#packages.{system}.default --priority 6",
|
|
]
|
|
|
|
assert run_hello(env) == "Hello World\n"
|
|
nix.nix(["profile", "install", "./flake2", "--priority", "100"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
nix.nix(["profile", "install", "./flake2", "--priority", "0"]).run().ok()
|
|
assert run_hello(env) == "Hello World2\n"
|
|
nix.nix(["profile", "install", "./flake1", "--priority", "100"]).run().ok()
|
|
assert run_hello(env) == "Hello World2\n"
|
|
|
|
|
|
@with_files({"flake1": flake})
|
|
class TestProfileContentUpgrade(ProfileTestBase):
|
|
@pytest.fixture(autouse=True)
|
|
def upgrade(self, nix: Nix, env: ManagedEnv, setup): # noqa: ANN001, ARG002
|
|
(env.dirs.home / "flake1/who").write_text("NixOS")
|
|
(env.dirs.home / "flake1/version").write_text("2.0")
|
|
|
|
nix.nix(["profile", "upgrade", "flake1"]).run().ok()
|
|
|
|
def test_upgrade_succeeded(self, nix: Nix, env: ManagedEnv):
|
|
assert run_hello(env) == "Hello NixOS\n"
|
|
|
|
result = nix.nix(["profile", "history"]).run().ok().stdout_s
|
|
assert f"packages.{system}.default: 1.0, 1.0-man -> 2.0, 2.0-man" in result
|
|
|
|
def test_diff_closures_works(self, nix: Nix):
|
|
assert nix.nix(["profile", "diff-closures"]).run().stdout_s == dedent("""\
|
|
Version 1 -> 2:
|
|
profile-test: 1.0 → 2.0
|
|
""")
|
|
|
|
def test_rollback_works(self, nix: Nix, env: ManagedEnv):
|
|
nix.nix(["profile", "rollback"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
|
|
|
|
@with_files({"flake1": flake} | get_global_asset_pack("user-envs"))
|
|
def test_uninstall(nix: Nix, env: ManagedEnv):
|
|
nix.nix_env(["-f", "./user-envs.nix", "-i", "foo-1.0"]).run().ok()
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
|
|
assert (env.dirs.home / ".nix-profile/bin/foo").exists()
|
|
assert "removed 1 packages" in nix.nix(["profile", "remove", "foo"]).run().ok().stderr_s
|
|
assert not (env.dirs.home / ".nix-profile/bin/foo").exists()
|
|
assert "foo: 1.0 -> ∅" in nix.nix(["profile", "history"]).run().ok().stdout_s
|
|
assert "Version 1 -> 2" in nix.nix(["profile", "diff-closures"]).run().ok().stdout_s
|
|
|
|
|
|
@with_files({"flake1": flake})
|
|
def test_output_override(nix: Nix, env: ManagedEnv):
|
|
profile = env.dirs.home / ".nix-profile"
|
|
|
|
nix.nix(["profile", "install", "./flake1^*"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
assert (profile / "share/man").exists()
|
|
assert (profile / "include").exists()
|
|
|
|
assert (env.dirs.home / "flake1/who").write_text("Lix")
|
|
nix.nix(["profile", "upgrade", "flake1"]).run().ok()
|
|
assert run_hello(env) == "Hello Lix\n"
|
|
assert (profile / "share/man").exists()
|
|
assert (profile / "include").exists()
|
|
|
|
assert "removed 1 packages" in nix.nix(["profile", "remove", "flake1"]).run().ok().stderr_s
|
|
nix.nix(["profile", "install", "./flake1^man"]).run().ok()
|
|
assert not (profile / "bin/hello").exists()
|
|
assert (profile / "share/man").exists()
|
|
assert not (profile / "include").exists()
|
|
|
|
|
|
@with_files({"flake1": flake, "flake2": flake})
|
|
def test_conflict_resolution_cppnix_8284(nix: Nix):
|
|
path = build(nix, "./flake1^out")
|
|
nix.nix(["profile", "install", path]).run().ok()
|
|
|
|
expr = f'(builtins.getFlake "./flake2").packages.{system}.default'
|
|
nix.nix(["profile", "install", "--impure", "--expr", expr]).run().expect(1)
|
|
|
|
|
|
class TestKeepEnvDerivations:
|
|
@with_files({"flake1": flake})
|
|
def test_keep_derivations_enabled(self, nix: Nix, env: ManagedEnv):
|
|
nix.settings["keep-env-derivations"] = "true"
|
|
nix.settings["keep-derivations"] = "false" # NOTE: This defaults to `true`.
|
|
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
res = nix.nix(["eval", "--raw", "./flake1#default.drvPath"]).run().ok()
|
|
drv_path = Path(res.stdout_s.strip())
|
|
|
|
assert drv_path.exists()
|
|
|
|
# The derivation should NOT be collected because `keep-env-derivations` is true
|
|
nix.nix(["store", "gc"]).run().ok()
|
|
|
|
assert drv_path.exists()
|
|
|
|
@with_files({"flake1": flake})
|
|
def test_keep_derivations_disabled(self, nix: Nix, env: ManagedEnv):
|
|
nix.settings["keep-derivations"] = "false" # NOTE: This defaults to `true`
|
|
|
|
nix.nix(["profile", "install", "./flake1", "-L"]).run().ok()
|
|
assert run_hello(env) == "Hello World\n"
|
|
|
|
res = nix.nix(["eval", "--raw", "./flake1#default.drvPath"]).run().ok()
|
|
drv_path = Path(res.stdout_s.strip())
|
|
|
|
assert drv_path.exists()
|
|
|
|
# The derivation should be collected because `keep-env-derivations` is false AND `keep-derivations` is false.
|
|
res = nix.nix(["store", "gc"]).run().ok()
|
|
|
|
assert not drv_path.exists()
|