mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(git): resolve a coordination root's repo from its product
Branch->project resolution (_project_slug_for_branch, _workspace_for_branch) read task.project_id, which is null for a coordination root — so root-level git ops (the root->master PR, the CEO merge) could not resolve a workspace. A new _project_for_task falls through to the product's first distinct repo (monorepo => the single repo) when project_id is null. Purely additive: a task with a project_id resolves exactly as before; only the previously- unresolvable root case changes.
This commit is contained in:
+27
-4
@@ -1876,13 +1876,37 @@ class GitService(BaseService):
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def _project_for_task(self, task: Any) -> Any | None:
|
||||
"""Resolve the Project whose repo a task's branch lives in.
|
||||
|
||||
A normal task carries ``project_id``. A coordination root carries a
|
||||
product (cell->repo map) but no project of its own — its
|
||||
``feature/main_pm/{root}`` integration branch lives in the product's
|
||||
repo(s). Fall through to the product's first distinct repo (monorepo =>
|
||||
the single repo) so root-level git ops (create_pr root->master, the CEO
|
||||
merge) resolve a workspace. Purely additive: a task WITH project_id
|
||||
resolves exactly as before.
|
||||
"""
|
||||
project_service = get_project_service(self.session)
|
||||
if task.project_id is not None:
|
||||
return await project_service.get(UUID(str(task.project_id)))
|
||||
product_id = getattr(task, "product_id", None)
|
||||
if product_id is None:
|
||||
return None
|
||||
from roboco.services.product import get_product_service
|
||||
|
||||
product_service = get_product_service(self.session)
|
||||
project_ids = await product_service.distinct_project_ids(UUID(str(product_id)))
|
||||
if not project_ids:
|
||||
return None
|
||||
return await project_service.get(project_ids[0])
|
||||
|
||||
async def _project_slug_for_branch(self, branch_name: str) -> str | None:
|
||||
"""Resolve project slug via the task that owns the branch."""
|
||||
task = await self._task_for_branch(branch_name)
|
||||
if task is None:
|
||||
return None
|
||||
project_service = get_project_service(self.session)
|
||||
project = await project_service.get(UUID(str(task.project_id)))
|
||||
project = await self._project_for_task(task)
|
||||
return project.slug if project else None
|
||||
|
||||
@staticmethod
|
||||
@@ -1920,8 +1944,7 @@ class GitService(BaseService):
|
||||
task = await self._task_for_branch(branch_name)
|
||||
if task is None:
|
||||
raise NotFoundError("Branch", branch_name)
|
||||
project_service = get_project_service(self.session)
|
||||
project = await project_service.get(UUID(str(task.project_id)))
|
||||
project = await self._project_for_task(task)
|
||||
if project is None:
|
||||
raise NotFoundError("Project", str(task.project_id))
|
||||
workspace_agent_id = self._resolve_workspace_agent_id(task, actor_agent_id)
|
||||
|
||||
@@ -98,6 +98,34 @@ async def test_project_slug_for_branch_none_when_no_task() -> None:
|
||||
assert await svc._project_slug_for_branch("missing") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_project_for_task_resolves_coordination_root_via_product() -> None:
|
||||
"""A project-less coordination root resolves its repo from the product map."""
|
||||
pid = uuid4()
|
||||
fake_project = MagicMock(slug="roboco")
|
||||
task = MagicMock(project_id=None, product_id=uuid4())
|
||||
svc = _service()
|
||||
product_svc = MagicMock(distinct_project_ids=AsyncMock(return_value=[pid]))
|
||||
with (
|
||||
_patch_project_service(fake_project),
|
||||
patch("roboco.services.product.get_product_service", return_value=product_svc),
|
||||
):
|
||||
out = await svc._project_for_task(task)
|
||||
assert out is fake_project
|
||||
product_svc.distinct_project_ids.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_project_for_task_uses_project_id_when_present() -> None:
|
||||
"""A normal task resolves by project_id exactly as before (additive change)."""
|
||||
fake_project = MagicMock(slug="roboco")
|
||||
task = MagicMock(project_id=uuid4(), product_id=None)
|
||||
svc = _service()
|
||||
with _patch_project_service(fake_project):
|
||||
out = await svc._project_for_task(task)
|
||||
assert out is fake_project
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# diff: derives parent + invokes git diff
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user