feat(registry): hard-cut META_REPO -> REGISTRY_REPO; wire mirror into startup/webhook/sweep
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+6
-5
@@ -28,6 +28,7 @@ from . import (
|
||||
api_notifications,
|
||||
api_prs,
|
||||
auth,
|
||||
projects as projects_mod,
|
||||
db,
|
||||
device_trust as device_trust_mod,
|
||||
docs as docs_mod,
|
||||
@@ -765,7 +766,7 @@ def make_router(
|
||||
# Read the proposed entry file from the head branch.
|
||||
slug = row["rfc_slug"]
|
||||
head = row["head_branch"]
|
||||
result = await gitea.read_file(config.gitea_org, config.meta_repo, f"rfcs/{slug}.md", ref=head)
|
||||
result = await gitea.read_file(config.gitea_org, (projects_mod.default_content_repo(config) or ""), f"rfcs/{slug}.md", ref=head)
|
||||
entry_payload: dict[str, Any] | None = None
|
||||
if result:
|
||||
text, _sha = result
|
||||
@@ -855,7 +856,7 @@ def make_router(
|
||||
pr = await bot.open_idea_pr(
|
||||
user.as_actor(),
|
||||
org=config.gitea_org,
|
||||
meta_repo=config.meta_repo,
|
||||
meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
slug=slug,
|
||||
file_contents=contents,
|
||||
pr_title=pr_title,
|
||||
@@ -931,7 +932,7 @@ def make_router(
|
||||
await bot.merge_idea_pr(
|
||||
user.as_actor(),
|
||||
org=config.gitea_org,
|
||||
meta_repo=config.meta_repo,
|
||||
meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=pr_number,
|
||||
slug=row["rfc_slug"],
|
||||
)
|
||||
@@ -951,7 +952,7 @@ def make_router(
|
||||
await bot.decline_idea_pr(
|
||||
user.as_actor(),
|
||||
org=config.gitea_org,
|
||||
meta_repo=config.meta_repo,
|
||||
meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=pr_number,
|
||||
slug=row["rfc_slug"],
|
||||
comment=body.comment,
|
||||
@@ -975,7 +976,7 @@ def make_router(
|
||||
await bot.withdraw_idea_pr(
|
||||
user.as_actor(),
|
||||
org=config.gitea_org,
|
||||
meta_repo=config.meta_repo,
|
||||
meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=pr_number,
|
||||
slug=row["rfc_slug"],
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ from fastapi import APIRouter, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod, funder, models_resolver
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod, funder, models_resolver, projects as projects_mod
|
||||
from .bot import Bot
|
||||
from .config import Config
|
||||
from .gitea import Gitea, GiteaError
|
||||
@@ -1153,7 +1153,7 @@ def make_router(
|
||||
|
||||
def _repo_for(rfc, branch: str = "main") -> tuple[str, str]:
|
||||
if _is_meta_target(rfc, branch):
|
||||
return config.gitea_org, config.meta_repo
|
||||
return config.gitea_org, (projects_mod.default_content_repo(config) or "")
|
||||
owner, repo = rfc["repo"].split("/", 1)
|
||||
return owner, repo
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ from fastapi import APIRouter, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from . import auth, cache, db, entry as entry_mod
|
||||
from . import auth, cache, db, entry as entry_mod, projects as projects_mod
|
||||
from .bot import Actor, Bot
|
||||
from .config import Config
|
||||
from .gitea import Gitea, GiteaError
|
||||
@@ -345,7 +345,7 @@ def make_router(
|
||||
# graduation PR's update_file call and the body to carry through
|
||||
# unchanged (meta-only keeps the body in the entry, §13.3).
|
||||
fetched = await gitea.read_file(
|
||||
config.gitea_org, config.meta_repo, f"rfcs/{slug}.md", ref="main",
|
||||
config.gitea_org, (projects_mod.default_content_repo(config) or ""), f"rfcs/{slug}.md", ref="main",
|
||||
)
|
||||
if fetched is None:
|
||||
raise HTTPException(409, f"Meta entry rfcs/{slug}.md not found on main")
|
||||
@@ -479,7 +479,7 @@ def make_router(
|
||||
raise HTTPException(409, f"A claim PR is already open: #{already['pr_number']}")
|
||||
|
||||
fetched = await gitea.read_file(
|
||||
config.gitea_org, config.meta_repo, f"rfcs/{slug}.md", ref="main",
|
||||
config.gitea_org, (projects_mod.default_content_repo(config) or ""), f"rfcs/{slug}.md", ref="main",
|
||||
)
|
||||
if fetched is None:
|
||||
raise HTTPException(409, f"Meta entry rfcs/{slug}.md not found on main")
|
||||
@@ -495,7 +495,7 @@ def make_router(
|
||||
try:
|
||||
pr = await bot.open_claim_pr(
|
||||
viewer.as_actor(),
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
slug=slug,
|
||||
new_file_contents=new_contents, prior_sha=meta_sha,
|
||||
)
|
||||
@@ -601,7 +601,7 @@ def make_router(
|
||||
|
||||
async def _read_meta_entry(slug: str) -> tuple[entry_mod.Entry, str]:
|
||||
fetched = await gitea.read_file(
|
||||
config.gitea_org, config.meta_repo, f"rfcs/{slug}.md", ref="main",
|
||||
config.gitea_org, (projects_mod.default_content_repo(config) or ""), f"rfcs/{slug}.md", ref="main",
|
||||
)
|
||||
if fetched is None:
|
||||
raise HTTPException(409, f"Meta entry rfcs/{slug}.md not found on main")
|
||||
@@ -656,7 +656,7 @@ async def _orchestrate(
|
||||
try:
|
||||
pr = await bot.open_graduation_pr(
|
||||
actor,
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
slug=state.slug,
|
||||
new_file_contents=graduated_contents,
|
||||
prior_sha=meta_file_sha,
|
||||
@@ -676,7 +676,7 @@ async def _orchestrate(
|
||||
try:
|
||||
await bot.merge_graduation_pr(
|
||||
actor,
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=state.new_pr_number,
|
||||
head_branch=state.graduation_branch or "",
|
||||
slug=state.slug, rfc_id=state.rfc_id,
|
||||
@@ -738,7 +738,7 @@ async def _cleanup_unmerged(
|
||||
try:
|
||||
await bot.close_graduation_pr(
|
||||
actor,
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=state.new_pr_number,
|
||||
head_branch=state.graduation_branch or "",
|
||||
slug=state.slug, reason="graduation merge failed",
|
||||
@@ -751,7 +751,7 @@ async def _cleanup_unmerged(
|
||||
await bot.delete_branch(
|
||||
actor,
|
||||
owner=config.gitea_org,
|
||||
repo=config.meta_repo,
|
||||
repo=(projects_mod.default_content_repo(config) or ""),
|
||||
branch=branch_name,
|
||||
slug=state.slug,
|
||||
action_kind="delete_post_merge_branch",
|
||||
@@ -861,7 +861,7 @@ async def _run_state_flip(
|
||||
try:
|
||||
pr = await bot.open_retire_flip_pr(
|
||||
actor,
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
slug=slug, new_file_contents=new_contents, prior_sha=prior_sha,
|
||||
verb=verb, target_state=target_state,
|
||||
)
|
||||
@@ -872,7 +872,7 @@ async def _run_state_flip(
|
||||
try:
|
||||
await bot.merge_retire_flip_pr(
|
||||
actor,
|
||||
org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=pr_number, head_branch=head_branch,
|
||||
slug=slug, verb=verb,
|
||||
)
|
||||
@@ -881,12 +881,12 @@ async def _run_state_flip(
|
||||
# accumulate (mirrors graduation's `_cleanup_unmerged`).
|
||||
try:
|
||||
await bot.close_graduation_pr(
|
||||
actor, org=config.gitea_org, meta_repo=config.meta_repo,
|
||||
actor, org=config.gitea_org, meta_repo=(projects_mod.default_content_repo(config) or ""),
|
||||
pr_number=pr_number, head_branch=head_branch,
|
||||
slug=slug, reason=f"{verb} merge failed",
|
||||
)
|
||||
await bot.delete_branch(
|
||||
actor, owner=config.gitea_org, repo=config.meta_repo,
|
||||
actor, owner=config.gitea_org, repo=(projects_mod.default_content_repo(config) or ""),
|
||||
branch=head_branch, slug=slug,
|
||||
action_kind="delete_post_merge_branch",
|
||||
reason=f"{verb} merge failed",
|
||||
|
||||
@@ -23,7 +23,7 @@ from typing import Any
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod, funder, models_resolver, rfc_links
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod, funder, models_resolver, projects as projects_mod, rfc_links
|
||||
from .bot import Bot
|
||||
from .config import Config
|
||||
from .gitea import Gitea, GiteaError
|
||||
@@ -691,7 +691,7 @@ def make_router(
|
||||
|
||||
def _owner_repo(rfc) -> tuple[str, str]:
|
||||
if _is_meta_resident(rfc):
|
||||
return config.gitea_org, config.meta_repo
|
||||
return config.gitea_org, (projects_mod.default_content_repo(config) or "")
|
||||
owner, repo = rfc["repo"].split("/", 1)
|
||||
return owner, repo
|
||||
|
||||
|
||||
+20
-4
@@ -27,7 +27,7 @@ import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
from . import db, entry as entry_mod
|
||||
from . import db, entry as entry_mod, projects as projects_mod, registry as registry_mod
|
||||
from .config import Config
|
||||
from .gitea import Gitea, GiteaError
|
||||
|
||||
@@ -40,7 +40,11 @@ async def refresh_meta_repo(config: Config, gitea: Gitea) -> None:
|
||||
Idempotent. Safe to call on every meta-repo webhook and on every
|
||||
reconciler sweep.
|
||||
"""
|
||||
org, repo = config.gitea_org, config.meta_repo
|
||||
org = config.gitea_org
|
||||
repo = projects_mod.default_content_repo(config)
|
||||
if not repo:
|
||||
log.warning("refresh_meta_repo: default project has no content_repo yet; skipping")
|
||||
return
|
||||
try:
|
||||
files = await gitea.list_dir(org, repo, "rfcs", ref="main")
|
||||
except GiteaError as e:
|
||||
@@ -328,7 +332,11 @@ async def refresh_meta_branches(config: Config, gitea: Gitea) -> None:
|
||||
structurally `edit/<slug>/<auto-name>` per §9.5, with dashes in place
|
||||
of slashes per the §19.2 path-routing candidate.
|
||||
"""
|
||||
org, repo = config.gitea_org, config.meta_repo
|
||||
org = config.gitea_org
|
||||
repo = projects_mod.default_content_repo(config)
|
||||
if not repo:
|
||||
log.warning("refresh_meta_branches: default project has no content_repo yet; skipping")
|
||||
return
|
||||
try:
|
||||
branches = await gitea.list_branches(org, repo)
|
||||
except GiteaError as e:
|
||||
@@ -445,7 +453,11 @@ async def refresh_meta_pulls(config: Config, gitea: Gitea) -> None:
|
||||
`On-behalf-of:` trailer from the PR body, then to the raw Gitea
|
||||
login as last resort.
|
||||
"""
|
||||
org, repo = config.gitea_org, config.meta_repo
|
||||
org = config.gitea_org
|
||||
repo = projects_mod.default_content_repo(config)
|
||||
if not repo:
|
||||
log.warning("refresh_meta_pulls: default project has no content_repo yet; skipping")
|
||||
return
|
||||
repo_full = f"{org}/{repo}"
|
||||
try:
|
||||
open_pulls = await gitea.list_pulls(org, repo, state="open")
|
||||
@@ -671,6 +683,10 @@ class Reconciler:
|
||||
async def sweep(self) -> None:
|
||||
log.info("reconciler: starting sweep")
|
||||
try:
|
||||
try:
|
||||
await registry_mod.refresh_registry(self._config, self._gitea)
|
||||
except Exception:
|
||||
log.exception("reconciler: registry refresh failed; keeping last-good projects")
|
||||
await refresh_meta_repo(self._config, self._gitea)
|
||||
await refresh_meta_branches(self._config, self._gitea)
|
||||
await refresh_meta_pulls(self._config, self._gitea)
|
||||
|
||||
+5
-11
@@ -32,7 +32,6 @@ class Config:
|
||||
gitea_bot_user: str
|
||||
gitea_bot_token: str
|
||||
gitea_org: str
|
||||
meta_repo: str
|
||||
registry_repo: str
|
||||
oauth_client_id: str
|
||||
oauth_client_secret: str
|
||||
@@ -45,14 +44,15 @@ class Config:
|
||||
anthropic_api_key: str = ""
|
||||
google_api_key: str = ""
|
||||
openai_api_key: str = ""
|
||||
default_project_id: str = ""
|
||||
|
||||
@property
|
||||
def redirect_uri(self) -> str:
|
||||
return f"{self.app_url}/auth/callback"
|
||||
|
||||
@property
|
||||
def meta_repo_full(self) -> str:
|
||||
return f"{self.gitea_org}/{self.meta_repo}"
|
||||
def registry_repo_full(self) -> str:
|
||||
return f"{self.gitea_org}/{self.registry_repo}"
|
||||
|
||||
|
||||
def load_config() -> Config:
|
||||
@@ -80,14 +80,7 @@ def load_config() -> Config:
|
||||
gitea_bot_user=_required("GITEA_BOT_USER"),
|
||||
gitea_bot_token=_required("GITEA_BOT_TOKEN"),
|
||||
gitea_org=_required("GITEA_ORG"),
|
||||
meta_repo=_optional("META_REPO", "meta"),
|
||||
# §22.2: the multi-project registry repo the framework reads to learn
|
||||
# which projects exist. Optional through Slice M1 — the registry
|
||||
# *mirror* lands in M3; until then the default project (seeded by
|
||||
# migration 026 + the §22.13 startup backfill) is the only project,
|
||||
# and its content_repo comes from META_REPO. Becomes required when
|
||||
# the mirror lands and META_REPO is retired.
|
||||
registry_repo=_optional("REGISTRY_REPO"),
|
||||
registry_repo=_required("REGISTRY_REPO"),
|
||||
oauth_client_id=_required("OAUTH_CLIENT_ID"),
|
||||
oauth_client_secret=_required("OAUTH_CLIENT_SECRET"),
|
||||
app_url=_optional("APP_URL", "http://localhost:8000").rstrip("/"),
|
||||
@@ -99,4 +92,5 @@ def load_config() -> Config:
|
||||
anthropic_api_key=_optional("ANTHROPIC_API_KEY"),
|
||||
google_api_key=_optional("GOOGLE_API_KEY"),
|
||||
openai_api_key=_optional("OPENAI_API_KEY"),
|
||||
default_project_id=_optional("DEFAULT_PROJECT_ID"),
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from . import db
|
||||
from . import db, projects as projects_mod
|
||||
from .bot import Bot
|
||||
from .config import Config
|
||||
|
||||
@@ -299,7 +299,7 @@ async def _delete_branch_via_bot(
|
||||
log.warning("hygiene: cannot delete %s/%s — slug missing from cache", slug, branch)
|
||||
return False
|
||||
if not rfc["repo"]:
|
||||
owner, repo = config.gitea_org, config.meta_repo
|
||||
owner, repo = config.gitea_org, (projects_mod.default_content_repo(config) or "")
|
||||
elif "/" in rfc["repo"]:
|
||||
owner, repo = rfc["repo"].split("/", 1)
|
||||
else:
|
||||
|
||||
+18
-2
@@ -31,6 +31,7 @@ from . import (
|
||||
projects,
|
||||
providers as providers_mod,
|
||||
ratelimit,
|
||||
registry as registry_mod,
|
||||
turnstile,
|
||||
webhooks,
|
||||
)
|
||||
@@ -100,8 +101,23 @@ async def lifespan(app: FastAPI):
|
||||
config = load_config()
|
||||
db.run_migrations(config)
|
||||
db.init(config)
|
||||
projects.seed_default_project(config) # §22.13 step 1
|
||||
gitea = Gitea(config)
|
||||
# §22.2: mirror the registry before anything reads projects/content_repo.
|
||||
# First boot has no last-good rows, so a missing/invalid registry is fatal
|
||||
# (loud-fail per separation-of-concerns); the reconciler sweep keeps it
|
||||
# fresh thereafter and tolerates a later bad PR.
|
||||
try:
|
||||
await registry_mod.refresh_registry(config, gitea)
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
f"registry mirror failed at startup ({config.registry_repo_full}/projects.yaml): {e}"
|
||||
) from e
|
||||
if projects.default_content_repo(config) is None:
|
||||
raise RuntimeError(
|
||||
f"registry does not describe the default project "
|
||||
f"{projects.resolved_default_id(config)!r} (no content_repo). "
|
||||
f"Add it to {config.registry_repo_full}/projects.yaml."
|
||||
)
|
||||
bot = Bot(gitea)
|
||||
reconciler = cache.Reconciler(config, gitea)
|
||||
digest_sched = digest.DigestScheduler()
|
||||
@@ -130,7 +146,7 @@ async def lifespan(app: FastAPI):
|
||||
reconciler.start()
|
||||
digest_sched.start()
|
||||
hygiene_sched.start()
|
||||
log.info("RFC app started — meta repo %s/%s", config.gitea_org, config.meta_repo)
|
||||
log.info("RFC app started — registry %s", config.registry_repo_full)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
||||
+29
-27
@@ -1,11 +1,9 @@
|
||||
"""Project registry — the §22 multi-project layer.
|
||||
|
||||
A deployment hosts one or more projects (§22.1). Through Slice M1 the only
|
||||
project is the migration-seeded `default` one (the N=1 case, §22.13); the git
|
||||
registry mirror that lets a deployment declare more projects lands in M3 and
|
||||
will grow this module. For now this holds just the §22.13 step-1 backfill:
|
||||
completing the default project's row from deployment config, which pure-SQL
|
||||
migration 026 could not read.
|
||||
A deployment hosts one or more projects (§22.1). The git registry mirror
|
||||
that lets a deployment declare projects lands in M3 and drives this module.
|
||||
`seed_default_project` (the §22.13 META_REPO backfill) is retired in M3;
|
||||
the registry mirror (`registry.refresh_registry`) is authoritative.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -15,26 +13,30 @@ from .config import Config
|
||||
DEFAULT_PROJECT_ID = "default"
|
||||
|
||||
|
||||
def seed_default_project(config: Config) -> None:
|
||||
"""Fill the default project's content_repo from config.
|
||||
def resolved_default_id(config: Config) -> str:
|
||||
"""The id of the deployment's bootstrap/default project. Plan A: always
|
||||
'default' (the re-stamp to a config slug rides Plan B). The config knob is
|
||||
read here so Plan B can flip the resolution without touching call sites."""
|
||||
return config.default_project_id.strip() or DEFAULT_PROJECT_ID
|
||||
|
||||
Migration 026 seeds `default` with content_repo NULL because SQL cannot
|
||||
read the environment. This sets it from META_REPO (the pre-multi-project
|
||||
single-corpus repo) the first time the configured app boots, so the row
|
||||
accurately names the corpus the existing rows belong to. Idempotent: only
|
||||
touches the row while content_repo is still unset, so a later registry
|
||||
mirror (M3) that has populated it is never clobbered.
|
||||
|
||||
The display `name` is deliberately left as-is: the deployment's
|
||||
user-visible name lives in the frontend build (VITE_APP_NAME) today and
|
||||
moves to the registry + GET /api/deployment in M3 (§22.9); the backend has
|
||||
no authoritative name to backfill from yet.
|
||||
"""
|
||||
db.conn().execute(
|
||||
"""
|
||||
UPDATE projects
|
||||
SET content_repo = ?, updated_at = datetime('now')
|
||||
WHERE id = ? AND content_repo IS NULL
|
||||
""",
|
||||
(config.meta_repo, DEFAULT_PROJECT_ID),
|
||||
)
|
||||
def default_content_repo(config: Config) -> str | None:
|
||||
"""The content repo the single-corpus mirror reads, from the default
|
||||
project's row (filled by the registry mirror). Replaces the retired
|
||||
META_REPO. None until the registry mirror has run."""
|
||||
row = db.conn().execute(
|
||||
"SELECT content_repo FROM projects WHERE id = ?",
|
||||
(resolved_default_id(config),),
|
||||
).fetchone()
|
||||
return row["content_repo"] if row and row["content_repo"] else None
|
||||
|
||||
|
||||
def project_initial_state(project_id: str) -> str:
|
||||
"""§22.4b landing state for new entries in a project. Defaults to
|
||||
'super-draft' for an unknown/unset row (the safe, today's-flow default)."""
|
||||
row = db.conn().execute(
|
||||
"SELECT initial_state FROM projects WHERE id = ?", (project_id,)
|
||||
).fetchone()
|
||||
if row is None or not row["initial_state"]:
|
||||
return "super-draft"
|
||||
return row["initial_state"]
|
||||
|
||||
+11
-8
@@ -16,7 +16,7 @@ import os
|
||||
|
||||
from fastapi import APIRouter, Header, HTTPException, Request
|
||||
|
||||
from . import cache, db
|
||||
from . import cache, db, projects as projects_mod, registry as registry_mod
|
||||
from .config import Config
|
||||
from .gitea import Gitea
|
||||
|
||||
@@ -79,9 +79,17 @@ def make_router(config: Config, gitea: Gitea) -> APIRouter:
|
||||
except Exception:
|
||||
payload = {}
|
||||
repo_full = (payload.get("repository") or {}).get("full_name") or ""
|
||||
meta_full = f"{config.gitea_org}/{config.meta_repo}"
|
||||
registry_full = f"{config.gitea_org}/{config.registry_repo}"
|
||||
meta_repo = projects_mod.default_content_repo(config)
|
||||
meta_full = f"{config.gitea_org}/{meta_repo}" if meta_repo else None
|
||||
try:
|
||||
if repo_full == meta_full or not repo_full:
|
||||
if repo_full == registry_full:
|
||||
# §22.2: a registry-repo push re-mirrors the projects table.
|
||||
try:
|
||||
await registry_mod.refresh_registry(config, gitea)
|
||||
except registry_mod.RegistryError:
|
||||
log.exception("registry webhook: invalid projects.yaml; keeping last-good")
|
||||
elif meta_full and (repo_full == meta_full or not repo_full):
|
||||
await cache.refresh_meta_repo(config, gitea)
|
||||
await cache.refresh_meta_branches(config, gitea)
|
||||
await cache.refresh_meta_pulls(config, gitea)
|
||||
@@ -90,11 +98,6 @@ def make_router(config: Config, gitea: Gitea) -> APIRouter:
|
||||
if slug:
|
||||
await cache.refresh_rfc_repo(config, gitea, slug)
|
||||
else:
|
||||
# v0.18.0: the proposal's "unknown-repo logging"
|
||||
# gesture — a hook on a fork or a stale repo binding
|
||||
# used to silently 200-OK here, hiding the
|
||||
# misconfiguration. Now the operator sees it in
|
||||
# the log.
|
||||
log.info(
|
||||
"webhook received for unknown repo: repo_full=%s event=%s "
|
||||
"(no cached_rfcs row matched; hook may be on a fork or stale)",
|
||||
|
||||
Reference in New Issue
Block a user