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 -t bearer token. Headers X-Forwarded-User and X-Forwarded-Access-Token are 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 sub claim.

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.