Compare commits
1 Commits
fix-flake-
...
push-trllk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62a1f1a9a9 |
@@ -8,15 +8,8 @@
|
||||
roles.default = {
|
||||
|
||||
perInstance.nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.garage.enable = lib.mkDefault true;
|
||||
|
||||
systemd.services.garage.serviceConfig = {
|
||||
LoadCredential = [
|
||||
"rpc_secret_path:${config.clan.core.vars.generators.garage-shared.files.rpc_secret.path}"
|
||||
|
||||
@@ -49,7 +49,7 @@ def get_latest_commit_info() -> tuple[str, str]:
|
||||
def open_editor_for_pr() -> tuple[str, str]:
|
||||
"""Open editor to get PR title and description. First line is title, rest is description."""
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w+", suffix="COMMIT_EDITMSG", delete=False
|
||||
mode="w+", suffix=".txt", delete=False
|
||||
) as temp_file:
|
||||
temp_file.write("\n")
|
||||
temp_file.write("# Please enter the PR title on the first line.\n")
|
||||
@@ -266,7 +266,6 @@ Examples:
|
||||
)
|
||||
|
||||
create_parser.add_argument(
|
||||
"-a",
|
||||
"--auto",
|
||||
action="store_true",
|
||||
help="Skip editor and use commit message automatically",
|
||||
|
||||
@@ -134,10 +134,6 @@ def install_machine(opts: InstallOptions) -> None:
|
||||
|
||||
if opts.debug:
|
||||
cmd.append("--debug")
|
||||
|
||||
# Add nix options to nixos-anywhere
|
||||
cmd.extend(opts.nix_options)
|
||||
|
||||
cmd.append(host.target)
|
||||
if opts.use_tor:
|
||||
# nix copy does not support tor socks proxy
|
||||
|
||||
@@ -55,6 +55,12 @@ def upload_sources(machine: Machine, ssh: Remote) -> str:
|
||||
is_local_input(node) for node in flake_data["locks"]["nodes"].values()
|
||||
)
|
||||
|
||||
# Construct the remote URL with proper parameters for Darwin
|
||||
remote_url = f"ssh://{ssh.target}"
|
||||
# MacOS doesn't come with a proper login shell for ssh and therefore doesn't have nix in $PATH as it doesn't source /etc/profile
|
||||
if machine._class_ == "darwin":
|
||||
remote_url += "?remote-program=bash -lc 'exec nix-daemon --stdio'"
|
||||
|
||||
if not has_path_inputs:
|
||||
# Just copy the flake to the remote machine, we can substitute other inputs there.
|
||||
path = flake_data["path"]
|
||||
@@ -62,7 +68,7 @@ def upload_sources(machine: Machine, ssh: Remote) -> str:
|
||||
[
|
||||
"copy",
|
||||
"--to",
|
||||
f"ssh://{ssh.target}",
|
||||
remote_url,
|
||||
"--no-check-sigs",
|
||||
path,
|
||||
]
|
||||
@@ -84,7 +90,7 @@ def upload_sources(machine: Machine, ssh: Remote) -> str:
|
||||
"flake",
|
||||
"archive",
|
||||
"--to",
|
||||
f"ssh://{ssh.target}",
|
||||
remote_url,
|
||||
"--json",
|
||||
flake_url,
|
||||
]
|
||||
|
||||
@@ -9,7 +9,6 @@ from tempfile import NamedTemporaryFile
|
||||
from typing import Any
|
||||
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.nix import nix_config
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -320,7 +319,9 @@ class FlakeCacheEntry:
|
||||
# strings need to be checked if they are store paths
|
||||
# if they are, we store them as a dict with the outPath key
|
||||
# this is to mirror nix behavior, where the outPath of an attrset is used if no further key is specified
|
||||
elif isinstance(value, str) and self._is_store_path(value):
|
||||
elif isinstance(value, str) and value.startswith(
|
||||
os.environ.get("NIX_STORE_DIR", "/nix/store")
|
||||
):
|
||||
assert selectors == []
|
||||
self.value = {"outPath": FlakeCacheEntry(value)}
|
||||
|
||||
@@ -334,68 +335,14 @@ class FlakeCacheEntry:
|
||||
msg = f"Cannot insert {value} into cache, already have {self.value}"
|
||||
raise TypeError(msg)
|
||||
|
||||
def _is_store_path(self, value: str) -> bool:
|
||||
"""Check if a string is a nix store path."""
|
||||
# A store path is any path that has "store" as one of its parent directories
|
||||
# and contains a hash-prefixed name after it
|
||||
path_parts = Path(value).parts
|
||||
|
||||
try:
|
||||
store_idx = path_parts.index("store")
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# Check if there's at least one more component after "store"
|
||||
if store_idx + 1 < len(path_parts):
|
||||
# Check if the component after store looks like a nix store item
|
||||
# (starts with a hash)
|
||||
store_item = path_parts[store_idx + 1]
|
||||
# Basic check: nix store items typically start with a hash
|
||||
return len(store_item) > 32 and "-" in store_item
|
||||
|
||||
return False
|
||||
|
||||
def _normalize_store_path(self, store_path: str) -> Path | None:
|
||||
"""
|
||||
Normalize a store path to use the current NIX_STORE_DIR.
|
||||
Returns None if the path is not a valid store path.
|
||||
"""
|
||||
# Extract the store item (hash-name) from the path
|
||||
path_parts = Path(store_path).parts
|
||||
|
||||
# Find the index of "store" in the path
|
||||
try:
|
||||
store_idx = path_parts.index("store")
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
if store_idx + 1 < len(path_parts):
|
||||
store_item = path_parts[store_idx + 1]
|
||||
|
||||
# Get the current store path
|
||||
# Check if we're using a test store first
|
||||
test_store = os.environ.get("CLAN_TEST_STORE")
|
||||
if test_store:
|
||||
# In test mode, the store is at CLAN_TEST_STORE/nix/store
|
||||
current_store = str(Path(test_store) / "nix" / "store")
|
||||
else:
|
||||
# Otherwise use nix config
|
||||
config = nix_config()
|
||||
current_store = config.get("store", "/nix/store")
|
||||
|
||||
return Path(current_store) / store_item if current_store else None
|
||||
|
||||
return None
|
||||
|
||||
def is_cached(self, selectors: list[Selector]) -> bool:
|
||||
selector: Selector
|
||||
|
||||
# for store paths we have to check if they still exist, otherwise they have to be rebuild and are thus not cached
|
||||
if isinstance(self.value, str) and self._is_store_path(self.value):
|
||||
normalized_path = self._normalize_store_path(self.value)
|
||||
if normalized_path:
|
||||
return normalized_path.exists()
|
||||
return False
|
||||
if isinstance(self.value, str) and self.value.startswith(
|
||||
os.environ.get("NIX_STORE_DIR", "/nix/store")
|
||||
):
|
||||
return Path(self.value).exists()
|
||||
|
||||
# if self.value is not dict but we request more selectors, we assume we are cached and an error will be thrown in the select function
|
||||
if isinstance(self.value, str | float | int | None):
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
"""Test flake cache with chroot stores."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from clan_lib.flake.flake import FlakeCache, FlakeCacheEntry
|
||||
|
||||
|
||||
def test_flake_cache_with_chroot_store(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test that flake cache works correctly with chroot stores."""
|
||||
# Create a mock store path
|
||||
normal_store = "/nix/store"
|
||||
chroot_store = str(tmp_path / "nix" / "store")
|
||||
|
||||
# Create the chroot store directory
|
||||
Path(chroot_store).mkdir(parents=True)
|
||||
|
||||
# Create a fake derivation in the chroot store with proper nix store format
|
||||
fake_drv = "abcd1234abcd1234abcd1234abcd1234-test-package"
|
||||
fake_store_path = f"{chroot_store}/{fake_drv}"
|
||||
Path(fake_store_path).mkdir(parents=True)
|
||||
|
||||
# Test 1: Cache entry with normal store path
|
||||
cache = FlakeCache()
|
||||
|
||||
# Insert a store path that doesn't exist in the normal store
|
||||
non_existent_path = f"{normal_store}/{fake_drv}"
|
||||
cache.insert({"test": {"package": non_existent_path}}, "")
|
||||
|
||||
# Without chroot store, this should be uncached (path doesn't exist)
|
||||
assert not cache.is_cached("test.package")
|
||||
|
||||
# Test 2: Set CLAN_TEST_STORE to chroot store parent
|
||||
# CLAN_TEST_STORE should point to the parent of nix/store
|
||||
monkeypatch.setenv("CLAN_TEST_STORE", str(tmp_path))
|
||||
|
||||
# Create a new cache with the chroot store
|
||||
cache2 = FlakeCache()
|
||||
|
||||
# Insert the same path but now it should use chroot store
|
||||
cache2.insert({"test": {"package": fake_store_path}}, "")
|
||||
|
||||
# This should be cached because the path exists in chroot store
|
||||
assert cache2.is_cached("test.package")
|
||||
|
||||
# Test 3: Cache persistence with chroot store
|
||||
cache_file = tmp_path / "cache.json"
|
||||
cache2.save_to_file(cache_file)
|
||||
|
||||
# Load cache in a new instance
|
||||
cache3 = FlakeCache()
|
||||
cache3.load_from_file(cache_file)
|
||||
|
||||
# Should still be cached with chroot store
|
||||
assert cache3.is_cached("test.package")
|
||||
|
||||
# Test 4: Cache validation fails when chroot store changes
|
||||
monkeypatch.setenv("CLAN_TEST_STORE", "/different")
|
||||
|
||||
# Same cache should now be invalid
|
||||
assert not cache3.is_cached("test.package")
|
||||
|
||||
|
||||
def test_flake_cache_entry_store_path_validation() -> None:
|
||||
"""Test that FlakeCacheEntry correctly validates store paths."""
|
||||
# Test with default store
|
||||
entry = FlakeCacheEntry()
|
||||
|
||||
# Insert a non-existent store path with proper format
|
||||
fake_path = "/nix/store/abcd1234abcd1234abcd1234abcd1234-fake-package"
|
||||
entry.insert(fake_path, [])
|
||||
|
||||
# Should not be cached because path doesn't exist
|
||||
assert not entry.is_cached([])
|
||||
|
||||
# Test with environment variable
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create nix/store structure
|
||||
store_dir = Path(tmpdir) / "nix" / "store"
|
||||
store_dir.mkdir(parents=True)
|
||||
|
||||
# Create a fake store path with proper format
|
||||
fake_drv = "test1234test1234test1234test1234-package"
|
||||
fake_path_obj = store_dir / fake_drv
|
||||
fake_path_obj.mkdir()
|
||||
fake_path = str(fake_path_obj)
|
||||
|
||||
# Set CLAN_TEST_STORE to parent of store dir
|
||||
old_env = os.environ.get("CLAN_TEST_STORE")
|
||||
try:
|
||||
os.environ["CLAN_TEST_STORE"] = str(tmpdir)
|
||||
|
||||
entry2 = FlakeCacheEntry()
|
||||
entry2.insert(str(fake_path), [])
|
||||
|
||||
# Should be cached because path exists
|
||||
assert entry2.is_cached([])
|
||||
|
||||
finally:
|
||||
if old_env is None:
|
||||
os.environ.pop("CLAN_TEST_STORE", None)
|
||||
else:
|
||||
os.environ["CLAN_TEST_STORE"] = old_env
|
||||
Reference in New Issue
Block a user