generated from coulomb/repo-seed
REUSE-WP-0019-T02: hub recompose staleness tracking + Forgejo webhook
Some checks failed
ci / validate-registry (push) Has been cancelled
Some checks failed
ci / validate-registry (push) Has been cancelled
reuse_surface/hub/store.py: compose_state table tracking composed_at/stale, updated only on a *forced* recompose (refresh=true, webhook, future scheduled fallback) -- a plain GET still serves current best-effort data but never silently reports itself as freshly composed. reuse_surface/hub/webhooks.py: constant-time HMAC-SHA256 signature verification (fails closed on an empty/unconfigured secret) and path-only push-payload inspection (never parses file content, per design principle 2 -- webhook only decides whether to trigger a pull-based recompose). New endpoint POST /v1/webhooks/forgejo, accepting both X-Forgejo-Signature and X-Gitea-Signature headers since sibling repos migrate independently. GET /v1/federated and POST /v1/federated/compose now share an asyncio.Lock with the webhook so concurrent recompose triggers coalesce instead of overlapping. No separate /v1/recompose route was added -- POST /v1/federated/compose already did that job from earlier hub work; specs/FederationHubAPI.md now documents this explicitly instead of duplicating it. 28 new pytest cases (128 total pass). Live-verified: ran the actual hub service locally and sent a real HMAC-signed webhook over HTTP, confirming the full webhook-to-recompose path, signature rejection, and the irrelevant-path no-op. Does NOT deploy to the live reuse.coulomb.social hub -- that needs a container rebuild/push/k8s rollout, a separate production-deployment action out of scope here without explicit sign-off. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,19 +113,48 @@ Implemented:
|
||||
|
||||
```task
|
||||
id: REUSE-WP-0019-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "691eb32a-6f20-4a2a-b9ff-0ae427b659aa"
|
||||
```
|
||||
|
||||
- Hub service (`reuse_surface/serve`): `POST /v1/recompose` (token-auth) —
|
||||
marks index stale and triggers recompose from registered raw URLs
|
||||
- `POST /v1/webhooks/forgejo`: validates Forgejo webhook signature
|
||||
(`X-Forgejo-Signature`, secret from env), accepts push events, triggers
|
||||
recompose only when the pushed commits touch `registry/indexes/`
|
||||
- Debounce/coalesce concurrent triggers; `GET /v1/federated` gains
|
||||
`composed_at` + `stale` fields
|
||||
- Extend `specs/FederationHubAPI.md`; pytest with signed fixture payloads
|
||||
**Note:** `POST /v1/federated/compose` (token-auth, triggers a real
|
||||
recompose) already existed from earlier hub work — no separate
|
||||
`/v1/recompose` route was added; the spec now documents this explicitly
|
||||
rather than duplicating a route that already does the job.
|
||||
|
||||
Implemented in `reuse_surface/hub/`:
|
||||
|
||||
- `store.py`: `compose_state` table (`composed_at`, `stale`), with
|
||||
`record_compose()`/`mark_stale()`/`get_compose_state()`. `composed_at`
|
||||
updates and `stale` clears only on a *forced* recompose (`refresh=true`,
|
||||
webhook, or future scheduled fallback) — a plain `GET` still serves
|
||||
current best-effort data but never silently reports itself as freshly
|
||||
composed
|
||||
- `webhooks.py`: `verify_signature` (constant-time HMAC-SHA256, fails
|
||||
closed on empty secret), `push_touches_registry_index` (path-only
|
||||
inspection of the push payload's `added`/`modified`/`removed` lists —
|
||||
never parses file content, per design principle 2)
|
||||
- `app.py`: `POST /v1/webhooks/forgejo` (accepts both `X-Forgejo-Signature`
|
||||
and `X-Gitea-Signature`, since repos migrate independently);
|
||||
`GET /v1/federated` and `POST /v1/federated/compose` now share an
|
||||
`asyncio.Lock` so concurrent recompose triggers (manual, webhook, future
|
||||
scheduled) coalesce instead of overlapping
|
||||
- `specs/FederationHubAPI.md` extended (§5.7-5.9, config table, error
|
||||
codes)
|
||||
- 28 new pytest cases (16 in `test_hub.py`, 12 in `test_webhooks.py`); 128
|
||||
total pass
|
||||
- **Live-verified**: ran the actual hub service locally
|
||||
(`reuse-surface serve`), sent a real HMAC-signed webhook payload over
|
||||
HTTP — confirmed `composed_at`/`stale` transitions, signature rejection,
|
||||
irrelevant-path no-op, and the full webhook-to-recompose path end to end
|
||||
|
||||
**Deployment boundary — deliberately not done:** this changes the hub
|
||||
*service source code* in this repo; it does **not** deploy to the live
|
||||
`reuse.coulomb.social` hub. That requires a container rebuild, registry
|
||||
push, and Kubernetes rollout — a production deployment action affecting a
|
||||
shared external service, out of scope for this task without explicit
|
||||
sign-off. See `docs/deploy/reuse-kubernetes.md`.
|
||||
|
||||
## Forgejo Webhook Rollout And Scheduled Fallback
|
||||
|
||||
|
||||
Reference in New Issue
Block a user