# RPM Tracker

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

---

RPM tracker owns the `rpm_tracker` schema in the shared `events` PostgreSQL
database. It stores upstream state and packaged version status for the dashboard
to read. See the [system proposal][proposal] for the collection and UI design.

## Features

1. Independent Alembic migration chain with an initializer and revision gate.
2. Transactional replacement of the current inventory with a generation check.
3. Read-only dashboard access to tracker data in the shared database.

## Prerequisites

Python 3.11 or newer and access to the shared PostgreSQL `events` database.
The migration user needs permission to create the `rpm_tracker` schema.

## Installation

```bash
cd rpm-tracker
pip install -e .
DATABASE_URL=postgresql://postgres@localhost:5432/events python -m rpm_tracker.init_db
```

The Alembic chain uses `rpm_tracker.alembic_version`, separate from other
services' migration histories and outside `public`. The migration files are
bundled in the installed `rpm_tracker` package. The initializer creates the
schema and applies pending migrations on each invocation; it is a no-op at
the current revision. Run it before deploying the dashboard API, and run it as
an init step before the future collector starts. A failed migration must prevent
collection. The dashboard read endpoint returns HTTP 503 until a migrated
schema compatible with its response model is available. Later tracker revisions
remain readable when they preserve the fields used by the dashboard.

## Usage

Create a SQLAlchemy engine with `rpm_tracker.db.get_engine()` for the storage
module, and dispose of it when collection finishes. The tracker-owned ORM
models mirror the Alembic tables; only the initializer migrates them. Call
`store.get_generation(engine)` before collecting; both it and
`store.ingest(engine, request)` require the bundled schema revision. Fetch the
complete RPM checkout and every required monitor release page before calling
`ingest()` with one status per package and the checked checkout SHA. A failed
fetch must not submit a partial inventory. `ingest()` locks the singleton sync
row, checks the expected generation, and replaces upstream and status rows in
one transaction. A failed write or conflicting generation leaves the previous
inventory unchanged. Stored rollout times are separate from inventory
replacement. The dashboard exposes `GET /api/rpm-upstream-state` to read the
sync, upstream, status, and rollout tables in one read-only snapshot; it does
not migrate or write tracker tables. The collector and OpenShift CronJob are
subsequent parts of the proposal.

## Configuration

| Variable       | Description                                 |
| -------------- | ------------------------------------------- |
| `DATABASE_URL` | Shared `events` PostgreSQL connection URL   |
| `POSTGRES_URL` | Connection URL fallback for the initializer |

## Development

```bash
cd rpm-tracker
pip install -e '.[dev]'
pytest
```

To run the PostgreSQL migration and atomic replacement tests, set
`DATABASE_URL` to a disposable PostgreSQL database before running the tracker
and dashboard tests. CI supplies `test_events`; the database-backed tests are
skipped when `DATABASE_URL` is unset. The tracker tests refuse to replace an
existing nonempty tracker inventory.

## License

This project is licensed under the GNU General Public License v3.0 or later.
See the [LICENSE](https://gitlab.com/redhat/hummingbird/tools/-/blob/main/LICENSE).

[proposal]: design/hummingbird-dashboard_rpm-rollout-tracker.md
