# Hummingbird MR Service

LLMS index: [llms.txt](/llms.txt) | Full content: [llms-full.txt](/llms-full.txt)

---

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).

```mermaid
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:

```bash
export MR_SERVICE_URL=https://mr.apps.cluster.example.com
export MR_SERVICE_TOKEN=$(oc whoami -t)
```

```bash
# 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

```bash
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

```bash
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][readme] for development workflows.

## License

This project is licensed under the GNU General Public License v3.0 or later —
see the [LICENSE][license] file for details.

[readme]: https://gitlab.com/redhat/hummingbird/tools/-/blob/main/README.md
[license]: https://gitlab.com/redhat/hummingbird/tools/-/blob/main/LICENSE
