diff --git a/ebuild/deps/manager.py b/ebuild/deps/manager.py index 64a31444..2f357d26 100644 --- a/ebuild/deps/manager.py +++ b/ebuild/deps/manager.py @@ -14,7 +14,7 @@ import subprocess import warnings from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import yaml @@ -30,6 +30,12 @@ # Known repo names KNOWN_REPOS = ("eos", "eboot") +# Sibling directory names. GitHub and local checkouts often use eBoot. +SIBLING_DIR_NAMES: Dict[str, Tuple[str, ...]] = { + "eos": ("eos",), + "eboot": ("eboot", "eBoot"), +} + # Environment variable names for path overrides ENV_PATH_VARS = { "eos": "EBUILD_EOS_PATH", @@ -46,7 +52,7 @@ class DepsManager: 2. Environment variables ``EBUILD_EOS_PATH`` / ``EBUILD_EBOOT_PATH`` 3. ``~/.ebuild/config.yaml`` custom ``path:`` override 4. ``~/.ebuild/repos//`` (cached git clone) - 5. Sibling directory ``..//`` (workspace layout) + 5. Sibling directory ``..//`` (workspace layout; ``eboot`` also tries ``eBoot``) 6. Embedded ``core//`` (legacy fallback — prints deprecation warning) """ @@ -204,11 +210,13 @@ def get_repo_path( if cached.is_dir(): return cached - # 5. Sibling directory + # 5. Sibling directory (try documented aliases; Linux is case-sensitive) if project_dir: - sibling = project_dir.parent / repo_name - if sibling.is_dir(): - return sibling + names = SIBLING_DIR_NAMES.get(repo_name, (repo_name,)) + for name in names: + sibling = project_dir.parent / name + if sibling.is_dir(): + return sibling # 6. Legacy embedded core// (deprecation warning) if project_dir: diff --git a/tests/ebuild/conftest.py b/tests/ebuild/conftest.py index 38ee8147..ea72fcac 100644 --- a/tests/ebuild/conftest.py +++ b/tests/ebuild/conftest.py @@ -17,8 +17,20 @@ # ── Repo roots (available to all tests via conftest) ───────── # tests/ebuild/conftest.py → parent = tests/ebuild/ → parent = tests/ → parent = repo root EBUILD_ROOT = Path(__file__).resolve().parent.parent.parent -EBOOT_ROOT = EBUILD_ROOT.parent / "eboot" -EOS_ROOT = EBUILD_ROOT.parent / "eos" + + +def _sibling_repo(*names: str) -> Path: + """First existing sibling dir, preferring the first name if none exist.""" + parent = EBUILD_ROOT.parent + for name in names: + candidate = parent / name + if candidate.is_dir(): + return candidate + return parent / names[0] + + +EBOOT_ROOT = _sibling_repo("eboot", "eBoot") +EOS_ROOT = _sibling_repo("eos") def _module_available(name: str) -> bool: diff --git a/tests/ebuild/test_deps_manager.py b/tests/ebuild/test_deps_manager.py new file mode 100644 index 00000000..16a186bf --- /dev/null +++ b/tests/ebuild/test_deps_manager.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2026 EoS Project + +"""Host tests for DepsManager sibling path resolution.""" + +from pathlib import Path + +import pytest + +from ebuild.deps.manager import DepsManager, SIBLING_DIR_NAMES + + +@pytest.fixture +def isolated_deps(tmp_path, monkeypatch): + """Keep get_repo_path off the real ~/.ebuild cache and config.""" + home = tmp_path / "ebuild-home" + repos = home / "repos" + config = home / "config.yaml" + monkeypatch.setattr("ebuild.deps.manager.ensure_ebuild_home", lambda: home) + monkeypatch.setattr("ebuild.deps.manager.EBUILD_CONFIG_PATH", config) + monkeypatch.setenv("EBUILD_REPOS_DIR", str(repos)) + monkeypatch.delenv("EBUILD_EOS_PATH", raising=False) + monkeypatch.delenv("EBUILD_EBOOT_PATH", raising=False) + home.mkdir() + repos.mkdir() + return tmp_path + + +def _case_sensitive_is_dir(self: Path) -> bool: + """Treat directory names as case-sensitive, as Linux does.""" + try: + names = {child.name for child in self.parent.iterdir()} + except OSError: + return False + return self.name in names + + +def test_eboot_aliases_include_github_casing(): + assert SIBLING_DIR_NAMES["eboot"] == ("eboot", "eBoot") + + +def test_sibling_eboot_resolves_camel_case(isolated_deps, monkeypatch): + workspace = isolated_deps / "ws" + project = workspace / "ebuild" + camel = workspace / "eBoot" + project.mkdir(parents=True) + camel.mkdir() + monkeypatch.setattr(Path, "is_dir", _case_sensitive_is_dir) + + resolved = DepsManager().get_repo_path("eboot", project_dir=project) + + assert resolved is not None + assert resolved.name == "eBoot" + + +def test_sibling_eboot_still_resolves_lowercase(isolated_deps, monkeypatch): + workspace = isolated_deps / "ws" + project = workspace / "ebuild" + lower = workspace / "eboot" + project.mkdir(parents=True) + lower.mkdir() + monkeypatch.setattr(Path, "is_dir", _case_sensitive_is_dir) + + resolved = DepsManager().get_repo_path("eboot", project_dir=project) + + assert resolved is not None + assert resolved.name == "eboot" + + +def test_sibling_eboot_missing_returns_none(isolated_deps, monkeypatch): + workspace = isolated_deps / "ws" + project = workspace / "ebuild" + project.mkdir(parents=True) + monkeypatch.setattr(Path, "is_dir", _case_sensitive_is_dir) + + resolved = DepsManager().get_repo_path("eboot", project_dir=project) + + assert resolved is None