feat(forge): per-user forge connections — keyring, host-keyed resolution, project pin (#2778)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 41s
CI & Build / integration (push) Successful in 37s
CI & Build / Python tests (push) Successful in 1m4s
CI & Build / Build & push image (push) Successful in 40s
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 41s
CI & Build / integration (push) Successful in 37s
CI & Build / Python tests (push) Successful in 1m4s
CI & Build / Build & push image (push) Successful in 40s
A forge token is a user's credential, not an instance's. The single admin-settings config is replaced by per-user keyring rows (one per forge host), and every server-side forge read runs on the PROJECT OWNER's keyring: - forge_connections table + projects.forge_connection_id pin (migration 0078, which also carries the existing admin config into the first admin's row and deletes the old setting keys — no legacy dual-read) - get_forge() replaced by get_forges(owner_id, project_id) -> ForgeSelector; resolve(repo) picks the connection whose host serves the repo. A pinned project uses ONLY its pinned connection; a stale pin (ownership moved) is ignored, never honored across users - env FORGE_* config survives as an implicit entry for admin owners only; a stored row for the same host beats it - consumers threaded: pull-time freshness (owner of the note), coverage (owner of the project), coverage routes' configured flag - routes: /api/settings/forge-connections CRUD + per-connection test (own-rows only, tokens never returned); /api/admin/forge shrinks to /api/admin/forge-webhook (secret only); PUT /api/projects/<id>/forge pins, owner-or-admin asking, owner's connections only - UI: Git Forges card moves to Settings -> Integrations as a connection list; webhook secret stays in the admin Config tab; owner-only forge select on the project coverage card - backups exclude forge_connections (credentials, api_keys precedent) and the pin, so restores fall back to keyring resolution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,11 +60,12 @@ class Config:
|
||||
# the MCP layer doesn't proxy web search (Claude has its own).
|
||||
SEARXNG_URL: str = os.environ.get("SEARXNG_URL", "")
|
||||
|
||||
# Git forge integration (#2689) — optional read access to the operator's
|
||||
# forge so snippet bodies can be fetched/verified server-side. Normally
|
||||
# configured in Settings → Config (stored as admin settings); these env
|
||||
# fallbacks exist so a deployment can keep the token in a Docker secret
|
||||
# instead of the database. DB value wins when both are set.
|
||||
# Git forge integration (#2689) — optional read access to a git forge so
|
||||
# snippet bodies can be fetched/verified server-side. Connections are
|
||||
# per-user keyring rows (#2778, Settings → Git forges); these env values
|
||||
# survive as an implicit keyring entry for ADMIN users' projects only, so
|
||||
# a deployment can keep the operator's token in a Docker secret instead
|
||||
# of the database. A stored row for the same host wins over the env entry.
|
||||
FORGE_KIND: str = os.environ.get("FORGE_KIND", "")
|
||||
FORGE_BASE_URL: str = os.environ.get("FORGE_BASE_URL", "").rstrip("/")
|
||||
FORGE_TOKEN: str = _read_secret("FORGE_TOKEN", "FORGE_TOKEN_FILE", "")
|
||||
|
||||
Reference in New Issue
Block a user