"""ORCH-090 (ADR-001 D8 / adr-0026): minimal Gitea branch helpers. Leaf module — a single never-raise helper used by the STOP-cancellation path to delete a cancelled task's REMOTE feature branch. Deliberately tiny and dependency -light (only ``config`` + ``httpx``) so it can be imported from the stage engine without cycles. Self-hosting safety (NFR-3): this helper deletes ONLY the named feature branch via the Gitea API. It NEVER touches ``main`` (a guard rejects it outright) and NEVER force-pushes — there is no push path here at all. """ import logging import httpx from .config import settings logger = logging.getLogger("orchestrator.gitea") # Branches that must never be deleted by an automated cancel (self-hosting safety). _PROTECTED_BRANCHES = {"main", "master"} def delete_remote_branch(repo: str, branch: str) -> bool: """Delete a remote feature branch in Gitea (never-raise). ``DELETE /api/v1/repos/{owner}/{repo}/branches/{branch}``. Used by ``stage_engine.cancel_task`` to reset a cancelled task's progress (D8). A 404 (branch already gone) is treated as success — the goal state (branch absent) is reached. Returns True iff the branch is confirmed absent after the call. Guards: * empty repo/branch -> no-op (False); * a protected branch (``main``/``master``) -> refused with an error log (NFR-3: STOP must never delete ``main``). Any network/API error is logged and swallowed (the worktree is cleaned locally regardless); returns False so the caller can note a best-effort miss. """ if not repo or not branch: return False if branch.strip().lower() in _PROTECTED_BRANCHES: logger.error( "delete_remote_branch REFUSED for protected branch %r in %s (self-hosting safety)", branch, repo, ) return False owner = settings.gitea_owner url = f"{settings.gitea_url}/api/v1/repos/{owner}/{repo}/branches/{branch}" headers = {"Authorization": f"token {settings.gitea_token}"} try: resp = httpx.delete(url, headers=headers, timeout=10) if resp.status_code in (204, 200): logger.info("Deleted remote branch %s in %s/%s", branch, owner, repo) return True if resp.status_code == 404: logger.info("Remote branch %s already absent in %s/%s", branch, owner, repo) return True logger.warning( "delete_remote_branch %s in %s/%s returned %s: %s", branch, owner, repo, resp.status_code, resp.text[:200], ) return False except Exception as e: # noqa: BLE001 - never-raise logger.warning("delete_remote_branch error for %s/%s/%s: %s", owner, repo, branch, e) return False