Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions ebuild/deps/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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",
Expand All @@ -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/<name>/`` (cached git clone)
5. Sibling directory ``../<name>/`` (workspace layout)
5. Sibling directory ``../<name>/`` (workspace layout; ``eboot`` also tries ``eBoot``)
6. Embedded ``core/<name>/`` (legacy fallback — prints deprecation warning)
"""

Expand Down Expand Up @@ -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/<name>/ (deprecation warning)
if project_dir:
Expand Down
16 changes: 14 additions & 2 deletions tests/ebuild/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
78 changes: 78 additions & 0 deletions tests/ebuild/test_deps_manager.py
Original file line number Diff line number Diff line change
@@ -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