# Release process

This document describes how `deslicer-cli` versions move from a git tag to installable artifacts on GitHub Releases, Homebrew, and crates.io.

## Overview

```
git tag vX.Y.Z
       │
       ▼
  release.yml ──► build 5 targets + SHA256 sidecars
       │          cosign keyless signatures (Sigstore OIDC)
       │          SLSA provenance attestation
       │          GitHub Release (archives + sigs + certs)
       │          floating tag v1 ──► latest v1.x compatible release
       │
       ├──► homebrew.yml (on: release published)
       │         └── PR to deslicer/homebrew-tap
       │
       └──► crates-publish.yml (on: release published)
                 └── cargo publish -p deslicer-cli (OIDC Trusted Publishing)
```

## Prerequisites

1. **Cargo.toml version** matches the tag (tag `v1.2.3` → `version = "1.2.3"`).
2. **crates.io Trusted Publishing** configured for `github.com/deslicer/cli` / `crates-publish.yml`.
3. **`HOMEBREW_TAP_TOKEN`** secret on the repo — PAT or GitHub App token with write access to `deslicer/homebrew-tap`.
4. Changelog / release notes prepared (GitHub auto-generates notes; edit after publish if needed).

## Step 1 — Tag and push

```bash
# Ensure main is clean and version bumped in Cargo.toml
git checkout main
git pull

git tag v1.0.0
git push origin v1.0.0
```

Tag pattern **`v*.*.*`** triggers `.github/workflows/release.yml`.

## Step 2 — release.yml (automatic)

### Build matrix

| Target | OS runner | Archive |
|--------|-----------|---------|
| `x86_64-unknown-linux-musl` | ubuntu-latest | `deslicer-x86_64-unknown-linux-musl.tar.gz` |
| `aarch64-unknown-linux-musl` | ubuntu-latest | `deslicer-aarch64-unknown-linux-musl.tar.gz` |
| `x86_64-apple-darwin` | macos-latest | `deslicer-x86_64-apple-darwin.tar.gz` |
| `aarch64-apple-darwin` | macos-latest | `deslicer-aarch64-apple-darwin.tar.gz` |
| `x86_64-pc-windows-msvc` | windows-latest | `deslicer-x86_64-pc-windows-msvc.zip` |

Each archive has a **`.sha256`** sidecar file.

### Signing and provenance

The `publish` job (only job with `id-token: write`):

- Installs **cosign** and signs each archive with **keyless Sigstore** (`cosign sign-blob --yes`). No secret beyond `GITHUB_TOKEN` for the Release itself.
- Creates/updates the **GitHub Release** attached to the tag.
- Moves the floating **`v1`** tag to the release commit (`git push origin refs/tags/v1 --force`).

The `provenance` job invokes **SLSA Level 3** generator (`slsa-framework/slsa-github-generator`). Pin the reusable workflow SHA before production use (see workflow TODO comment).

## Step 3 — Downstream publish (automatic on `release: published`)

### Homebrew (`homebrew.yml`)

- Downloads darwin + linux release tarballs.
- Computes SHA256 checksums.
- Opens a PR against [deslicer/homebrew-tap](https://github.com/deslicer/homebrew-tap) updating `Formula/deslicer.rb`.

Manual dispatch is also available:

```bash
gh workflow run homebrew.yml -f version=v1.0.0
```

### crates.io (`crates-publish.yml`)

- Verifies `Cargo.toml` version matches the release tag.
- Runs `cargo publish -p deslicer-cli --locked` using **Trusted Publishing** (GitHub OIDC → crates.io). No `CARGO_REGISTRY_TOKEN` secret.

## Floating `v1` tag policy

The **`v1`** tag always points at the latest **`v1.*.*`** release commit. It is force-moved on every stable v1.x publish so consumers can pin:

```bash
curl -fsSL .../releases/download/v1/deslicer-x86_64-unknown-linux-musl.tar.gz
```

Semver-breaking **`v2.0.0`** will introduce a separate floating `v2` tag policy when that major ships.

## Verification checklist

After a release:

- [ ] GitHub Release contains all five archives, `.sha256` files, and cosign `.sig`/`.cert` pairs
- [ ] `cosign verify-blob` succeeds against Sigstore for at least one artifact
- [ ] Homebrew tap PR merged (or dispatched manually)
- [ ] `deslicer-cli` version visible on https://crates.io/crates/deslicer-cli
- [ ] `v1` tag points to the new commit

## Rollback

1. **GitHub Release** — mark pre-release or delete the release (does not remove the git tag).
2. **Homebrew** — revert the tap PR or pin the previous formula version.
3. **crates.io** — yank the broken crate version (`cargo yank -p deslicer-cli --version X.Y.Z`); publish a patch release.
4. **`v1` tag** — manually reset to the last good commit if needed.

## CI-only validation

Every push/PR to `main` runs `.github/workflows/ci.yml` (fmt, clippy, tests). Dependency policy scanning (`cargo deny`, `cargo audit`) runs non-blocking until `deny.toml` lands.
