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
27 changes: 27 additions & 0 deletions ebuild/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2349,6 +2349,27 @@ def generate_boot(log: Logger, boot_yaml: str, output_dir: str) -> None:
# Dependency management commands
# ═══════════════════════════════════════════════════════════════

def _warn_cached_repo(log: Logger, name: str, repo_dir: Path) -> None:
"""Say plainly that ``setup`` reused an existing clone without pulling it.

``DepsManager.setup`` deliberately leaves an existing clone alone (it
must not move a pinned branch or tag), so a clone made weeks ago would
otherwise be reported exactly like a fresh one (issue #180).
"""
try:
result = subprocess.run(
["git", "-C", str(repo_dir), "log", "-1", "--format=%h %cs"],
capture_output=True, text=True, check=True,
)
where = f"at {result.stdout.strip()}"
except (subprocess.CalledProcessError, FileNotFoundError):
where = "at an unknown commit"
log.warning(
f" {name} was already cloned ({where}); setup does not pull. "
f"Run `ebuild repos update` to get the latest."
)


@cli.command()
@click.option("--eos-url", default=None, help="Git URL for eos repo (overrides default).")
@click.option("--eboot-url", default=None, help="Git URL for eboot repo (overrides default).")
Expand Down Expand Up @@ -2388,12 +2409,18 @@ def setup(

try:
log.step("Setting up eos...")
eos_cached = eos_path is None and (mgr.cache_dir / "eos").is_dir()
eos_dir = mgr.setup("eos", url=eos_url, branch=eos_branch, path=eos_path)
log.success(f" eos: {eos_dir}")
if eos_cached:
_warn_cached_repo(log, "eos", eos_dir)

log.step("Setting up eboot...")
eboot_cached = eboot_path is None and (mgr.cache_dir / "eboot").is_dir()
eboot_dir = mgr.setup("eboot", url=eboot_url, branch=eboot_branch, path=eboot_path)
log.success(f" eboot: {eboot_dir}")
if eboot_cached:
_warn_cached_repo(log, "eboot", eboot_dir)

log.success("Setup complete. Repos are ready.")
except Exception as e:
Expand Down
87 changes: 87 additions & 0 deletions tests/unit/test_setup_stale_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""`ebuild setup` must say when it reused an existing clone (issue #180).

DepsManager.setup() leaves an existing clone alone on purpose, so the CLI
is the only place that can tell the user the checkout was not updated.
"""

import subprocess
from pathlib import Path

import pytest
from click.testing import CliRunner

from ebuild.cli.commands import cli


def _git(repo: Path, *args: str) -> str:
return subprocess.run(
["git", "-C", str(repo), *args],
capture_output=True, text=True, check=True,
).stdout.strip()


def _make_clone(path: Path) -> str:
path.mkdir(parents=True)
_git(path, "init", "-q")
(path / "README").write_text("x\n", encoding="utf-8")
_git(path, "add", "README")
_git(path, "-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "-m", "init")
return _git(path, "log", "-1", "--format=%h %cs")


class _FakeManager:
"""Stands in for DepsManager: records calls, never touches the network."""

cache_root: Path = Path()

def __init__(self) -> None:
self.cache_dir = self.cache_root

def setup(self, name, url=None, branch=None, path=None):
if path:
return Path(path)
dest = self.cache_dir / name
dest.mkdir(parents=True, exist_ok=True) # a "fresh clone"
return dest


@pytest.fixture
def fake_manager(tmp_path, monkeypatch):
import ebuild.deps.manager as manager

_FakeManager.cache_root = tmp_path / "repos"
monkeypatch.setattr(manager, "DepsManager", _FakeManager)
return _FakeManager.cache_root


def test_fresh_clone_has_no_stale_warning(fake_manager):
result = CliRunner().invoke(cli, ["setup"])
assert result.exit_code == 0, result.output
assert "already cloned" not in result.output
assert "Setup complete" in result.output


def test_existing_clone_names_commit_and_update_command(fake_manager):
eos_head = _make_clone(fake_manager / "eos")
result = CliRunner().invoke(cli, ["setup"])
assert result.exit_code == 0, result.output
assert f"eos was already cloned (at {eos_head}); setup does not pull." in result.output
assert "ebuild repos update" in result.output
# eboot was freshly "cloned" in this run, so it gets no warning.
assert "eboot was already cloned" not in result.output


def test_existing_non_git_dir_still_warns(fake_manager):
(fake_manager / "eboot").mkdir(parents=True)
result = CliRunner().invoke(cli, ["setup"])
assert result.exit_code == 0, result.output
assert "eboot was already cloned (at an unknown commit)" in result.output


def test_linked_path_is_not_reported_as_cached(fake_manager, tmp_path):
_make_clone(fake_manager / "eos")
local = tmp_path / "my-eos"
local.mkdir()
result = CliRunner().invoke(cli, ["setup", "--eos-path", str(local)])
assert result.exit_code == 0, result.output
assert "eos was already cloned" not in result.output
Loading