# CI/CD & Release Automation Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add GitHub Actions CI (fmt/clippy/test/audit) and cargo-dist release automation (Linux x86_64 binary + crates.io publish) to the Quartermaster project.

**Architecture:** Two separate GitHub Actions workflows — `ci.yml` is hand-written with four parallel jobs, `release.yml` is generated by `dist init`. Cargo.toml gets build profiles and cargo-dist metadata. Justfile gets local convenience targets.

**Tech Stack:** GitHub Actions, cargo-dist 0.31.0, rustsec/audit-check, Swatinem/rust-cache, dtolnay/rust-toolchain

## Global Constraints

- Linux x86_64 only (single target: `x86_64-unknown-linux-gnu`)
- Rust stable toolchain
- CI triggers: `push` to `main` + `pull_request` targeting `main`
- Release triggers: `v*` tags + manual workflow dispatch
- `cargo audit` blocks PRs (no advisory pass-through)
- No installers (shell/powershell/homebrew) for v1

---

### Task 1: CI Workflow

**Files:**
- Create: `.github/workflows/ci.yml`
- Create: `audit.toml`

**Interfaces:**
- Consumes: nothing (standalone)
- Produces: CI pipeline that runs on PRs to main and pushes to main

- [ ] **Step 1: Create `.github/workflows/` directory**

```bash
mkdir -p .github/workflows
```

- [ ] **Step 2: Create `audit.toml`**

Create `audit.toml` at the repo root — the RustSec advisory ignore list:

```toml
[advisories]
ignore = [
    # "RUSTSEC-YYYY-NNNN",  # reason for accepting the risk
]
```

- [ ] **Step 3: Create `.github/workflows/ci.yml`**

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  CARGO_TERM_COLOR: always

jobs:
  fmt:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
        with:
          key: clippy
      - run: cargo clippy -- -D warnings

  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: test
      - run: cargo test

  audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
```

Notes for the implementer:
- `fmt` does not need `Swatinem/rust-cache` — it doesn't compile anything, just checks formatting.
- `clippy` and `test` each get their own cache key so they don't invalidate each other.
- `audit` uses the official RustSec action which handles installing and running `cargo audit` internally.
- The `${{ secrets.GITHUB_TOKEN }}` in the YAML must be written literally — it's a GitHub Actions expression, not a shell variable.

- [ ] **Step 4: Verify the workflow YAML is valid**

```bash
cd /home/anten/code/quartermaster
# Quick syntax check — ensure it parses as valid YAML
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))"
```

Expected: no output (clean parse).

If `python3` or the `yaml` module isn't available, use:
```bash
# Alternative: just check it's valid YAML with yq or similar
cat .github/workflows/ci.yml | head -5
```

Verify the structure looks correct (4 jobs, correct trigger branches).

- [ ] **Step 5: Verify CI checks pass locally**

Run each CI check locally to confirm they pass before pushing:

```bash
cargo fmt --check
cargo clippy -- -D warnings
cargo test
```

Expected: all three pass. If `cargo audit` is installed locally, also run `cargo audit`. If not, that's fine — the CI action handles installation.

- [ ] **Step 6: Commit**

```bash
git add .github/workflows/ci.yml audit.toml
git commit -m "ci: add GitHub Actions CI pipeline (fmt, clippy, test, audit)"
```

---

### Task 2: Cargo.toml Build Profiles & cargo-dist Metadata

**Files:**
- Modify: `Cargo.toml` (append build profiles and dist metadata at end of file)

**Interfaces:**
- Consumes: nothing (standalone)
- Produces: `[profile.release]`, `[profile.dist]`, `[workspace.metadata.dist]` sections that cargo-dist reads during `dist init`

- [ ] **Step 1: Add build profiles and cargo-dist metadata to `Cargo.toml`**

Append these sections to the end of `Cargo.toml` (after the `[dev-dependencies]` section):

```toml
[profile.release]
strip = true
codegen-units = 1

[profile.dist]
inherits = "release"
lto = "thin"

[workspace.metadata.dist]
cargo-dist-version = "0.31.0"
ci = "github"
installers = []
targets = ["x86_64-unknown-linux-gnu"]
dispatch-releases = true
```

- [ ] **Step 2: Verify Cargo.toml still parses correctly**

```bash
cargo check
```

Expected: compiles successfully (the new sections are valid TOML that cargo recognizes).

- [ ] **Step 3: Commit**

```bash
git add Cargo.toml
git commit -m "build: add release profiles and cargo-dist metadata"
```

---

### Task 3: Generate Release Workflow with cargo-dist

**Files:**
- Create: `.github/workflows/release.yml` (generated by `dist init`)
- Modify: `Cargo.toml` (cargo-dist may update its own metadata section)

**Interfaces:**
- Consumes: `[workspace.metadata.dist]` from Task 2
- Produces: `.github/workflows/release.yml` — the release workflow triggered by `v*` tags and workflow dispatch

- [ ] **Step 1: Install cargo-dist**

```bash
cargo install cargo-dist
```

Or via the installer script (faster):
```bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh
```

Verify installation:
```bash
dist --version
```

Expected: `dist 0.31.0` (or close to it).

- [ ] **Step 2: Run `dist init` to generate the release workflow**

```bash
cd /home/anten/code/quartermaster
dist init
```

This is interactive. Select:
- CI: **github** (should be pre-selected from Cargo.toml)
- Targets: **x86_64-unknown-linux-gnu** (should be pre-selected)
- Installers: **none** (should be pre-selected)

If it asks about other options, accept defaults.

This will:
1. Generate `.github/workflows/release.yml`
2. Potentially update `Cargo.toml` metadata (e.g., pin the exact cargo-dist version)

- [ ] **Step 3: Review the generated workflow**

Open `.github/workflows/release.yml` and verify:
- Triggers include `push: tags: ["v*"]`
- Triggers include `workflow_dispatch` (from `dispatch-releases = true`)
- Build target is `x86_64-unknown-linux-gnu`
- It produces tar.gz artifacts with checksums

- [ ] **Step 4: Review any Cargo.toml changes**

```bash
git diff Cargo.toml
```

cargo-dist may have adjusted the metadata section. That's expected. Verify the changes look reasonable (version pin, added fields).

- [ ] **Step 5: Test a local dist build (dry run)**

```bash
dist build
```

Expected: builds the `quma` binary in release mode and produces artifacts in `target/distrib/`. Verify the tar.gz and checksum files exist:

```bash
ls target/distrib/
```

Expected output should include something like:
```
quma-v0.1.0-x86_64-unknown-linux-gnu.tar.gz
quma-v0.1.0-x86_64-unknown-linux-gnu.tar.gz.sha256
dist-manifest.json
```

- [ ] **Step 6: Commit**

```bash
git add .github/workflows/release.yml Cargo.toml
git commit -m "ci: add cargo-dist release workflow"
```

---

### Task 4: Justfile Convenience Targets

**Files:**
- Modify: `Justfile` (add `audit` and `release-dry-run` targets)

**Interfaces:**
- Consumes: `cargo audit` (may need to be installed), `dist build` (installed in Task 3)
- Produces: `just audit` and `just release-dry-run` commands

- [ ] **Step 1: Add `audit` and `release-dry-run` targets to `Justfile`**

Add these two targets after the existing `serve` target at the end of the file:

```just
audit:
    cargo audit

release-dry-run:
    dist build
```

- [ ] **Step 2: Verify the Justfile parses**

```bash
just --list
```

Expected: output includes `audit` and `release-dry-run` in the target list alongside the existing targets.

- [ ] **Step 3: Commit**

```bash
git add Justfile
git commit -m "chore: add audit and release-dry-run justfile targets"
```

---

### Task 5: Verify End-to-End & Document Secrets

**Files:**
- No file changes — verification only

**Interfaces:**
- Consumes: all prior tasks
- Produces: confidence that everything works

- [ ] **Step 1: Run all CI checks locally**

```bash
just fmt
just clippy
just test
just audit
```

Expected: all pass. If `cargo audit` is not installed, install it first: `cargo install cargo-audit`.

- [ ] **Step 2: Verify dist dry-run works**

```bash
just release-dry-run
```

Expected: builds successfully, produces artifacts in `target/distrib/`.

- [ ] **Step 3: Verify file structure**

```bash
find .github -type f
ls audit.toml
```

Expected:
```
.github/workflows/ci.yml
.github/workflows/release.yml
audit.toml
```

- [ ] **Step 4: Remind about repository secrets**

After pushing, the following secret must be configured in GitHub repo settings (Settings → Secrets and variables → Actions → New repository secret):

| Secret | Purpose | How to get it |
|--------|---------|---------------|
| `CARGO_REGISTRY_TOKEN` | crates.io API token for `cargo publish` | https://crates.io/settings/tokens → New Token → select `publish-update` scope |

`GITHUB_TOKEN` is provided automatically by GitHub Actions — no configuration needed.

- [ ] **Step 5: Final commit (if any uncommitted changes remain)**

```bash
git status
```

If there are uncommitted changes from dist init or other adjustments:

```bash
git add -A
git commit -m "chore: finalize CI/CD and release automation setup"
```
