Hummingbird MR Service
Centralized MR lifecycle management service for Hummingbird. Receives patches from internal services, pushes branches, creates merge requests, and tracks their lifecycle through CI, approval, merge, and post-merge pipeline completion.
Features
- Work Item Submission — Internal services submit patches with metadata via REST API; idempotency keys prevent duplicate MRs
- Automated MR Creation — Worker claims pending items, pushes branches, and creates GitLab merge requests
- Lifecycle Tracking — Tracks MRs from submission through CI, approval, merge, post-merge pipeline, Konflux build/release, to completion
- Authentication — OAuth proxy for human/CLI access, Kubernetes ServiceAccount JWT validation for in-cluster service-to-service calls
Architecture
The service runs as a Kubernetes deployment alongside hummingbird-status and hummingbird-dashboard, sharing the same PostgreSQL database (with a service-specific Alembic migration chain).
flowchart LR
gl_events["GitLab\n(events)"] -- webhook --> fwd["gitlab-event-forwarder"]
fwd --> sns["SNS"]
sns -- "lifecycle events" --> mr["MR Service"]
clients["Internal services"] -- "POST /work-items" --> mr
mr -- "push branch,\ncreate MR" --> gl_push["GitLab\n(API)"]
mr -- "shared DB" --> pg[("PostgreSQL")]
Inbound: Internal services (e.g. dependency updaters, CVE remediation) submit work items (patch + metadata). The worker claims pending items and creates MRs via the GitLab API.
Lifecycle events: GitLab webhooks flow through the existing gitlab-event-forwarder → SNS pipeline. The MR service subscribes to track CI status, approvals, merges, and post-merge pipelines.
Work Item Lifecycle
The lifecycle uses a phase + dimensions model rather than a single linear state.
Phases
| Phase | Description |
|---|---|
pending |
Submitted, waiting for initial action |
active |
MR exists on GitLab, dimensions tracked |
merged |
MR merged, post-merge tracking active |
completed |
All dimensions satisfied |
failed |
Unrecoverable error (retryable) |
closed |
MR closed without merge |
Dimensions
Once a work item is active, independent dimensions track readiness
signals (CI status, approval, Konflux build/release). Dimensions are
stored in a separate work_item_statuses table with a composite key
(work_item_id, dimension, variant), allowing multiple instances per
dimension (e.g. per-component Konflux builds).
A pure-function decider derives the next action from the current phase and dimension statuses. A worker claims items with a pending action, executes it, and re-evaluates.
API
Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/api/work-items |
Submit a new work item |
GET |
/api/work-items |
List work items (filter by phase, source, project) |
GET |
/api/work-items/{id} |
Get a single work item |
POST |
/api/work-items/{id}/retry |
Retry a failed work item |
Authentication
Two tiers, both producing a caller identity for audit:
- Tier 1 (humans/CLI): OAuth proxy sidecar on port 8081. Authenticate
with
oc whoami -tbearer token. HeadersX-Forwarded-UserandX-Forwarded-Access-Tokenare forwarded to the app. - Tier 2 (in-cluster services): Kubernetes ServiceAccount projected
tokens validated locally via OIDC JWKS (no ClusterRoleBinding needed).
Caller identity extracted from JWT
subclaim.
API Documentation
Interactive API documentation is available without authentication:
/docs— Swagger UI/redoc— ReDoc/openapi.json— OpenAPI schema
CLI
Set the service URL and authentication token via environment variables:
export MR_SERVICE_URL=https://mr.apps.cluster.example.com
export MR_SERVICE_TOKEN=$(oc whoami -t)
# Submit a work item
hummingbird-mr-service submit \
--source test --package pkg --project org/group/project \
--change-kind sync --patch /path/to/file.patch \
--branch-name test/branch --title "Test MR" \
--idempotency-key test/1
# Check status
hummingbird-mr-service status --id <uuid>
# List work items
hummingbird-mr-service status --phase pending
--url can also be passed as a CLI argument to override the environment
variable. The token is read exclusively from MR_SERVICE_TOKEN to avoid
leaking credentials via process listings.
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
— | PostgreSQL connection URL |
SENTRY_DSN |
— | Sentry DSN for error reporting (optional) |
LOCAL_AUTH_USER |
— | For local dev, bypasses auth (optional) |
JWT_AUDIENCE |
hummingbird-mr-service |
Expected JWT aud claim (optional) |
MR_SERVICE_URL |
— | CLI: MR service URL |
MR_SERVICE_TOKEN |
— | CLI: Bearer token for authentication |
Development
Local Development
cd hummingbird-mr-service
./dev.sh db-start # Start local PostgreSQL (Hummingbird image)
./dev.sh db-migrate # Run Alembic migrations
./dev.sh start # Start the service
./dev.sh db-shell # PostgreSQL shell
./dev.sh db-stop # Stop PostgreSQL
./dev.sh db-reset # Stop and delete volume
Running Tests
cd hummingbird-mr-service
pip install -e ".[dev]"
python -m unittest discover tests
Tests use testcontainers[postgres] locally (auto-detects Podman) or a
CI-provided PostgreSQL sidecar via DATABASE_URL.
See the main README for development workflows.
License
This project is licensed under the GNU General Public License v3.0 or later — see the LICENSE file for details.