mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(conventions): keep the read clone current on a private repo
The read-clone refresh reused the orchestrator's token-less best-effort fetch, but _clone_repo scrubs the token from the remote URL — so on a PRIVATE repo the refresh fetch failed silently and the clone stayed frozen at clone-time, never seeing commits merged afterwards. The panel then showed "auto-derived defaults" for a project whose .roboco/conventions.yml was already on the default branch. Replace the refresh with a token-authenticated fetch + hard-reset to the default branch (the token is injected transiently into the fetch argv, mirroring the clone; the read clone is orchestrator-side and never mounted into an agent container). A public repo with no token still refreshes unauthenticated.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""WorkspaceService._sync_read_clone — the conventions read-clone refresh.
|
||||
|
||||
The read clone is hard-reset to the default branch on every refresh. The bug it
|
||||
fixes: the old refresh used a token-less fetch, so a private repo's clone stayed
|
||||
frozen at clone-time and never saw commits merged afterwards. This proves the
|
||||
refresh actually advances the clone to a post-clone commit.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from roboco.services.workspace import WorkspaceService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _git(cwd: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["git", *args], cwd=str(cwd), capture_output=True, text=True, check=False
|
||||
)
|
||||
|
||||
|
||||
def _commit(repo: Path, message: str) -> str:
|
||||
_git(repo, "add", "-A")
|
||||
_git(repo, "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", message)
|
||||
return _git(repo, "rev-parse", "HEAD").stdout.strip()
|
||||
|
||||
|
||||
def test_sync_read_clone_advances_to_a_post_clone_commit(tmp_path: Path) -> None:
|
||||
origin = tmp_path / "origin"
|
||||
origin.mkdir()
|
||||
_git(origin, "init", "-q", "-b", "master")
|
||||
(origin / "README.md").write_text("v1\n")
|
||||
_commit(origin, "first")
|
||||
|
||||
clone = tmp_path / "clone"
|
||||
_git(tmp_path, "clone", "-q", str(origin), str(clone))
|
||||
first = _git(clone, "rev-parse", "HEAD").stdout.strip()
|
||||
|
||||
# A new commit lands on origin AFTER the clone — the frozen-clone scenario.
|
||||
(origin / "NEW.txt").write_text("added later\n")
|
||||
second = _commit(origin, "second")
|
||||
assert first != second
|
||||
|
||||
# token=None: a file:// fetch needs no auth (mirrors a public-repo refresh);
|
||||
# for a private https repo the token would be injected into the fetch URL.
|
||||
WorkspaceService._sync_read_clone(clone, f"file://{origin}", "master", None)
|
||||
|
||||
assert _git(clone, "rev-parse", "HEAD").stdout.strip() == second
|
||||
assert (clone / "NEW.txt").is_file()
|
||||
|
||||
|
||||
def test_sync_read_clone_is_best_effort_on_unreachable_origin(tmp_path: Path) -> None:
|
||||
origin = tmp_path / "origin"
|
||||
origin.mkdir()
|
||||
_git(origin, "init", "-q", "-b", "master")
|
||||
(origin / "README.md").write_text("v1\n")
|
||||
_commit(origin, "first")
|
||||
clone = tmp_path / "clone"
|
||||
_git(tmp_path, "clone", "-q", str(origin), str(clone))
|
||||
head = _git(clone, "rev-parse", "HEAD").stdout.strip()
|
||||
|
||||
# A bogus origin must not raise — the refresh logs and leaves the clone as-is.
|
||||
WorkspaceService._sync_read_clone(clone, "file:///nonexistent/repo", "master", None)
|
||||
assert _git(clone, "rev-parse", "HEAD").stdout.strip() == head
|
||||
Reference in New Issue
Block a user