GitLab integration
Verifier: packages/core/src/verification/gitlab.ts (pure, test-injected) Webhook receiver: apps/mcp/src/routes/webhooks-gitlab.ts
Differences from GitHub
| Concern | GitHub | GitLab |
|---|---|---|
| Webhook auth | HMAC SHA-256 (X-Hub-Signature-256) | plain token (X-Gitlab-Token), constant-time compare |
| Delivery id header | X-GitHub-Delivery | X-Gitlab-Event-UUID |
| Event header | X-GitHub-Event | X-Gitlab-Event (string like Merge Request Hook) |
| Webhook scope | App-level (one per install) | Per-project (one per repo) |
| Auth method | GitHub App (JWT-based) | OAuth or PAT — both first-class |
| Self-hosted | not relevant for github.com | first-class — host + base_url + optional ca_bundle_pem per installation |
Verification
Contract on the task:
{ "v": 1, "kind": "gitlab_mr", "required_checks": ["lint", "unit-tests"] }verifyGitLabMr({ projectId, mrIid, requiredChecks, expectedFiles? }) maps required_checks to pipeline job names (not stage names). A manual job is treated as success so deploy-only manual steps don't block the verdict.
Verdict matrix:
| MR state | Required jobs | Verdict |
|---|---|---|
| opened | any | pending |
| closed | any | failed |
| merged | all green | passed |
| merged | one failure | failed |
| merged | one missing | pending (CI may still arrive post-merge) |
| merged | expectedFiles glob unmet | failed |
Self-hosted (PAT + custom CA)
The existing git_installations schema already covers:
host— the API base host (e.g.gitlab.acme.dev).auth_method—'oauth'or'pat'.access_token_encrypted— OAuth token OR PAT, AES-256-GCM encrypted.ca_bundle_pem— optional PEM for instances behind a private CA.
The runtime adapter (task 32) builds a gitbeaker client with an https.Agent injected when ca_bundle_pem is set — TLS verification stays on, the trust chain is just extended. We never set rejectUnauthorized: false.
Webhook token model
The env-level GITLAB_WEBHOOK_SECRET is the dev / single-tenant fallback. The production pattern stores a per-repo token at provision time inside repositories.webhook_secret_encrypted. Task 32's verification worker will swap the env compare for a per-row lookup keyed by (project_id, host) — the host disambiguates the same project_id across gitlab.com and one or more self-hosted instances.