# Local-release recipe (`mgf-fed release-local`)

> **Audience:** maintainer / QA running an end-to-end **local** release — validating a library
> (and its dependents) against locally-built artifacts before anything touches Codeberg.
> **Reaffirms:** REL-02 (a version is built once), REL-08 (smoke the actual wheel), PKG-01
> (the registry is the contract) in [`docs/standards/RELEASING.md`](../standards/RELEASING.md).
> **Tooling:** `mgf-fed release-local` (the federation orchestrator); `uv` for build + install.
> **History:** Codified 2026-06-11 alongside the two-mode release model (federation automation plan, Phase 2).

---

## The two-mode release model

A Magogi release has two modes, in this **order**:

1. **Local release** (this recipe) — build the wheel **once**, validate it end-to-end against
   the **local index** (`~/.mgf/local-index/`), and publish it there. Always available; no
   network, no rate-limit, no Codeberg.
2. **Remote release** (optional, later) — *promote the same wheel* to Codeberg. See
   [`remote-release.md`](remote-release.md).

Same version number in both modes — **build-once, promote** (REL-02). Local may lead in time;
remote is the eventual mirror. A dependent always resolves its mgf-* deps from the local index
first (so you can validate an unreleased change across the whole chain before publishing).

## When to run

- Validating a change to a library **and** its downstream dependents before a remote release.
- A QA/tester gate that needs the real built artifact (not just the source tree).
- Standing up a clean room: release the whole chain locally, in dependency-wave order.

## The gate (what `release-local` runs, per library, in wave order)

| # | Stage | What it checks |
|---|---|---|
| 1 | **gate** | static `ruff` lint + format (the pinned `ruff>=0.4,<0.16`) |
| 2 | **build** | `uv build --wheel` — build the artifact **once** (REL-02) |
| 3 | **validate** | `py.typed` present in the wheel (AP-09) + version coherence (wheel ↔ pyproject) |
| 4 | **smoke** | install the wheel in a throwaway env resolving **LOCAL-FIRST** (local index + PyPI, **not** the devpi index) — so mgf-* deps must already be locally released, which **enforces wave order** (REL-08) |
| 5 | **publish** | copy the validated wheel into `~/.mgf/local-index/` |

For the *full* gate (mypy / pytest matrix / vuln-scan / sbom) run `mgf-fed local-ci <repo>`
first — it executes the real `.woodpecker` pipeline in containers.

## Run it

```sh
# one library
mgf-fed release-local mgf-standard

# a library + its dependents (processed roots→leaves automatically)
mgf-fed release-local mgf-common mgf-http mgf-sqlalchemy

# inspect what's locally released
mgf-fed local-index
```

A dependent's **smoke** stage will fail with *"no local release of X — run `release-local X`
first"* if you skip a dependency — that failure **is** the wave-order guard. Release roots first.

## Then what

Once the chain is locally validated, either keep iterating locally, or **promote** the exact
same versions to Codeberg with [`remote-release.md`](remote-release.md) (`mgf-fed release-remote`).
