diff --git a/pkgs/clan-cli/clan_cli/templates.py b/pkgs/clan-cli/clan_cli/templates.py index 1f80b2a5f..1fc3d9c54 100644 --- a/pkgs/clan-cli/clan_cli/templates.py +++ b/pkgs/clan-cli/clan_cli/templates.py @@ -100,6 +100,9 @@ def get_clan_nix_attrset(clan_dir: Flake | None = None) -> ClanExports: [ "--json", "--impure", + "--option", + "flake-registry", + "", "--expr", eval_script, ] @@ -144,23 +147,29 @@ def copy_from_nixstore(src: Path, dest: Path) -> None: raise ClanError(msg) # Walk through the source directory - for root, _dirs, files in src.walk(): + for root, _dirs, files in src.walk(on_error=log.error): relative_path = Path(root).relative_to(src) dest_dir = dest / relative_path dest_dir.mkdir(exist_ok=True) log.debug(f"Creating directory '{dest_dir}'") # Set permissions for directories - dest_dir.chmod(stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) + dest_dir.chmod( + stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC | stat.S_IRGRP | stat.S_IXGRP + ) for file_name in files: src_file = Path(root) / file_name dest_file = dest_dir / file_name - # Copy the file - shutil.copy(src_file, dest_file) - - dest_file.chmod(stat.S_IWRITE | stat.S_IREAD) + if src_file.is_symlink(): + target = src_file.readlink() + dest_file.symlink_to(target) + log.debug(f"Created symlink '{dest_file}' -> '{target}'") + else: + # Copy the file + shutil.copy(src_file, dest_file) + dest_file.chmod(stat.S_IWRITE | stat.S_IREAD | stat.S_IRGRP) @dataclass diff --git a/pkgs/clan-cli/tests/test_clan_nix_attrset.py b/pkgs/clan-cli/tests/test_clan_nix_attrset.py index ab9bd3170..36da4fe36 100644 --- a/pkgs/clan-cli/tests/test_clan_nix_attrset.py +++ b/pkgs/clan-cli/tests/test_clan_nix_attrset.py @@ -5,9 +5,17 @@ from pathlib import Path from typing import Any import pytest +from clan_cli.cmd import run from clan_cli.flake import Flake from clan_cli.locked_open import locked_open -from clan_cli.templates import get_clan_nix_attrset +from clan_cli.nix import nix_command +from clan_cli.templates import ( + InputName, + TemplateName, + copy_from_nixstore, + get_clan_nix_attrset, + list_templates, +) from fixtures_flakes import FlakeForTest @@ -31,6 +39,64 @@ def nix_attr_tester( assert json.dumps(nix_attrset, indent=2) == json.dumps(expected, indent=2) +@pytest.mark.impure +def test_clan_core_templates( + test_flake_with_core: FlakeForTest, + monkeypatch: pytest.MonkeyPatch, + temporary_home: Path, +) -> None: + clan_dir = Flake(str(test_flake_with_core.path)) + nix_attrset = get_clan_nix_attrset(clan_dir) + clan_core_templates = nix_attrset["inputs"][InputName("clan-core")]["templates"][ + "clan" + ] + clan_core_template_keys = list(clan_core_templates.keys()) + + expected_templates = ["default", "flake-parts", "minimal", "minimal-flake-parts"] + assert clan_core_template_keys == expected_templates + vlist_temps = list_templates("clan", clan_dir) + list_template_keys = list(vlist_temps.inputs[InputName("clan-core")].keys()) + assert list_template_keys == expected_templates + + new_clan = temporary_home / "new_clan" + copy_from_nixstore( + Path( + vlist_temps.inputs[InputName("clan-core")][TemplateName("default")]["path"] + ), + new_clan, + ) + assert (new_clan / "flake.nix").exists() + assert (new_clan / "machines").is_dir() + assert (new_clan / "machines" / "jon").is_dir() + config_nix_p = new_clan / "machines" / "jon" / "configuration.nix" + assert (config_nix_p).is_file() + + # Test if we can write to the configuration.nix file + with config_nix_p.open("r+") as f: + data = f.read() + f.write(data) + + +def test_copy_from_nixstore_symlink( + monkeypatch: pytest.MonkeyPatch, temporary_home: Path +) -> None: + src = temporary_home / "src" + src.mkdir() + (src / "file.txt").write_text("magicstring!") + res = run(nix_command(["store", "add", str(src)])) + src_nix = Path(res.stdout.strip()) + src2 = temporary_home / "src2" + src2.mkdir() + (src2 / "file.txt").symlink_to(src_nix / "file.txt") + res = run(nix_command(["store", "add", str(src2)])) + src2_nix = Path(res.stdout.strip()) + dest = temporary_home / "dest" + copy_from_nixstore(src2_nix, dest) + assert (dest / "file.txt").exists() + assert (dest / "file.txt").read_text() == "magicstring!" + assert (dest / "file.txt").is_symlink() + + # Test Case 1: Minimal input with empty templates @pytest.mark.impure def test_clan_get_nix_attrset_case_1(