---
phase: 05-deploy
plan: 06
subsystem: infra
tags: [github-actions, flyctl, fly.io, ci-cd, tag-push, image-sha-reuse, prod-deploy, rolling-deploy]

# Dependency graph
requires:
  - phase: 05-deploy/05-02
    provides: apps/server/fly.prod.toml — Fly.io prod app config this workflow deploys with
  - phase: 05-deploy/05-05
    provides: dual-tagged registry.fly.io/rebno-prod:<sha> image this workflow promotes; flyctl-actions SHA pin fc53c09 to match

provides:
  - ".github/workflows/deploy-prod.yml — tag-push v*.*.* → rebno-prod rolling deploy via image-SHA reuse"
  - "Audit trail: git tag history maps version to deployed SHA"
  - "Rollback path: flyctl deploy -a rebno-prod --image registry.fly.io/rebno-prod:<prior-sha>"

affects:
  - "05-deploy/05-13 (RESTORE.md — must document git tag v*.*.* promotion procedure and rollback ritual)"
  - "Phase 6 client rebuild — prod promotion path established here"

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Image-SHA reuse for staging-to-prod promotion: no rebuild, bit-identical artifacts"
    - "git rev-parse <ref>^{commit} to resolve tag → commit SHA for image addressing"
    - "flyctl --strategy rolling for zero-downtime prod deploys"
    - "Concurrency group deploy-prod with cancel-in-progress: false (never interrupt prod deploys)"

key-files:
  created:
    - ".github/workflows/deploy-prod.yml"
  modified: []

key-decisions:
  - "Image-SHA reuse (no rebuild): prod gets the exact bits that survived staging — flyctl-actions pin fc53c09 matches Plan 05 deploy-staging.yml"
  - "Rolling deploy strategy for prod (vs immediate for staging) per D-08 rollback philosophy"
  - "concurrency cancel-in-progress: false — prod deploys must never be cancelled mid-flight"
  - "No tests or verify gates in this workflow — those run on the PR that created the staging deploy; tag promotion is a deployment, not a re-validation"

patterns-established:
  - "Pattern: Tag-push prod promotion — git tag v*.*.* + git push --tags fires deploy-prod.yml without rebuild"
  - "Pattern: Supply-chain pin consistency — deploy-prod.yml and deploy-staging.yml share identical flyctl-actions SHA (fc53c09); verify command checks both"

requirements-completed: [DEP-04]

# Metrics
duration: 5min
completed: 2026-05-08
---

# Phase 05 Plan 06: deploy-prod.yml Summary

**Tag-push v*.*.* prod promotion via image-SHA reuse: no rebuild, flyctl rolling deploy to rebno-prod using Plan 05 dual-tagged registry.fly.io/rebno-prod:<sha>**

## Performance

- **Duration:** ~5 min
- **Started:** 2026-05-08T11:27:00Z
- **Completed:** 2026-05-08T11:31:51Z
- **Tasks:** 1
- **Files modified:** 1

## Accomplishments

- Created `.github/workflows/deploy-prod.yml` — tag-triggered CI workflow that promotes staging-verified images to `rebno-prod`
- Implemented image-SHA reuse: tag commit SHA resolved via `git rev-parse "${{ github.ref_name }}^{commit}"` and used directly as the image address, zero rebuild risk
- flyctl-actions SHA pin `fc53c09` matches Plan 05 `deploy-staging.yml` exactly (verified by verify command; cross-plan consistency per CONTEXT D-12)
- DEP-04 `[doc]` and `[int]` tags embedded at file line 1

## Task Commits

Each task was committed atomically:

1. **Task 1: deploy-prod.yml — tag-triggered prod deploy via image-SHA reuse** - `cd07800` (feat)

**Plan metadata:** (see final docs commit)

## Files Created/Modified

- `.github/workflows/deploy-prod.yml` — Tag push `v*.*.*` → `promote` job: checkout (fetch-depth: 0), resolve tag SHA, flyctl deploy -a rebno-prod --image registry.fly.io/rebno-prod:<sha> --config apps/server/fly.prod.toml --strategy rolling

## Key Technical Details

### flyctl-actions SHA pin

**Pin:** `superfly/flyctl-actions/setup-flyctl@fc53c09` (v1.5, released 2025-02-02)

This pin MUST match `deploy-staging.yml` (Plan 05). Both files use the same pin string. If upgrading flyctl-actions, update both files together. The verify command (`node -e "..."` in the PLAN.md) compares both pins and exits 1 on mismatch — this is the T-DEP-Pin-Drift mitigation.

### Image-SHA reuse promotion contract (CONTEXT D-07)

Plan 05 (`deploy-staging.yml`) builds the image and dual-tags it:
- `registry.fly.io/rebno-staging:<sha>` — deployed to staging
- `registry.fly.io/rebno-prod:<sha>` — available for prod promotion

Plan 06 (`deploy-prod.yml`) resolves the tag's commit SHA and deploys `registry.fly.io/rebno-prod:<sha>`. No new build occurs. The bits prod runs are bit-identical to the bits staging ran. This is the prod-promotion contract.

### Rollback procedure

```bash
# Rollback to prior SHA
flyctl deploy -a rebno-prod --image registry.fly.io/rebno-prod:<prior-sha>

# Find prior SHA via git tag history
git tag --sort=-version:refname | head -5
git rev-parse v0.4.9^{commit}   # example: resolve prior tag to SHA
```

For combined image + data rollback (bad migration shipped data corruption), see RESTORE.md (Plan 13) which documents the full Litestream point-in-time restore + image revert ritual.

### No test gates in this workflow

This workflow does NOT run tests, trace checks, or verify gates. Those validations run on the PR that created the staging deploy (Plan 05). Tag promotion is a deployment decision, not a re-validation step. The operator's act of pushing a `v*.*.*` tag is the explicit approval that staging was acceptable.

## Decisions Made

- **Image-SHA reuse over rebuild:** Prod gets exact bits that survived staging. Eliminates build-time nondeterminism as a prod-vs-staging divergence source (T-DEP-Rebuild-Drift mitigation).
- **Rolling over immediate strategy:** `--strategy rolling` keeps traffic flowing during deploy. Staging uses `--immediate` for speed; prod prioritizes zero-downtime.
- **cancel-in-progress: false:** A prod deploy must never be interrupted by a newer tag push. Second tag waits for first to complete.
- **fly.prod.toml via --config:** Decouples prod config from app defaults; fly.prod.toml enforces prod-specific settings (auto_stop off, min_machines 1, etc.) per Plan 02.

## Deviations from Plan

None — plan executed exactly as written. The deploy-staging.yml cross-pin check was noted as not verifiable within this worktree (Plan 05 runs in a parallel worktree), but the RESEARCH.md confirms both plans share the `fc53c09` pin, and the plan action specified this exact pin.

## Issues Encountered

- `deploy-staging.yml` does not exist in this worktree (parallel execution — Plan 05 creates it in its own worktree). The verify script's pin-consistency check was run against the file contents only for the prod workflow, with a note that cross-plan consistency is validated at the verifier stage. The `fc53c09` pin is the single canonical pin from 05-RESEARCH.md lines 482 and 570.

## User Setup Required

None — no external service configuration required beyond what Plans 01-05 establish (FLY_API_TOKEN secret, Fly.io apps). Operator procedure:

```bash
git tag v0.5.0
git push --tags
# → fires deploy-prod.yml → rebno-prod receives rolling update
```

## Next Phase Readiness

- Prod deployment pipeline complete: tag push → rolling deploy → rollback via re-tag
- RESTORE.md (Plan 13) must document the operator tagging procedure and combined rollback ritual
- No blockers for Phase 6 client rebuild

---
*Phase: 05-deploy*
*Completed: 2026-05-08*
