feat(forge): Phase 1 — GitProvider seam, GitHub transport extracted (#571)

* feat(forge): Phase 0 — git_provider column + registration-time forge validation

Pointing a project at a GitLab/Gitea git_url used to fail silently, several
steps deep, at first PR. New pure policy module (foundation/policy/forge.py)
detects the provider from the git_url host and validates at the
ProjectService create/update chokepoint: github auto-detects and
auto-stamps, explicit git_provider=github is the GitHub Enterprise escape
hatch, gitlab/gitea are recognized-but-not-yet-supported, unknown hosts get
a loud rejection with guidance. An update changing git_url does NOT inherit
a stored auto-stamped provider (restating the override is required), so a
host swap can't smuggle the escape hatch past validation. Migration 075
adds the nullable projects.git_provider column; the panel project dialogs
show the detected forge. Phase 0 of the forge-providers spec.

* feat(forge): Phase 1 — GitProvider seam, GitHub transport extracted

roboco/services/forge/: base.py holds the pure contracts (RepoRef +
GitProvider ABC, stdlib-only — a later GitLabProvider is implemented by
reading this file alone), github.py the httpx transport (20 endpoint
methods behind one shared request-plumbing helper set), registry.py the
wiring (git_provider column -> provider, failing loud on gitlab/gitea).
GitService keeps its exact public surface and all response
classification; its 26 inline REST call sites route through a lazy
_forge property (several suites build GitService via __new__, so an
__init__-set attribute breaks them). github_provisioning and
release_executor ride the same provider. Zero behavior change — the
pre-existing suites pass unmodified; per-project provider resolution
lands with the second provider.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-18 19:11:56 +02:00
committed by GitHub
co-authored by Renn F
parent 7e01c0cecf
commit 461a6e1ae7
9 changed files with 886 additions and 384 deletions
+3 -1
View File
@@ -147,7 +147,9 @@ export const projectsApi = {
slug: project.slug,
git_url: project.git_url,
// Mock mode: mirror the backend's auto-detect (github.com -> github).
git_provider: project.git_provider ?? detectGithubProvider(project.git_url),
git_provider:
project.git_provider ??
(project.git_url.includes("github.com") ? "github" : null),
default_branch: project.default_branch ?? "main",
environments: project.environments ?? null,
protected_branches: project.protected_branches ?? ["main", "master"],