Add Phase 1I (Deploy pipeline + runbook) implementation plan

This commit is contained in:
2026-04-15 00:53:10 +03:00
parent 59d5a7314e
commit 0f4180de14
@@ -0,0 +1,106 @@
# Phase 1I — Deploy pipeline + runbook contracts
**Parent plan:** `2026-04-14-phase-1-foundation-master.md`, section "1I — Deploy pipeline + runbook contracts"
**Date:** 2026-04-14
---
## Overview
This sub-plan delivers the deploy pipeline contracts: Docker images for both build targets (standalone SSR and remote static), health endpoint, graceful shutdown handler, CI workflow template, and an operational runbook.
## Constraints
- Do NOT modify `modern.config.ts` (middleware registration is a future integration step)
- Health and shutdown modules export factory functions, not auto-registered middleware
- Coexist with the existing ASP.NET `Dockerfile` and `Dockerfile.local` (write `Dockerfile.react` and `Dockerfile.remote`)
- Do NOT touch `ClientApp/`, ASP.NET files, or `wwwroot/`
---
## Tasks
### Task 1 — `Dockerfile.react` (standalone SSR image)
**File:** `Dockerfile.react` (repo root)
Multi-stage build:
1. Stage 1 (`deps`): Node 24 base, enable corepack pnpm, copy `package.json` + `pnpm-lock.yaml`, run `pnpm install --frozen-lockfile`
2. Stage 2 (`build`): copy `src/`, config files, run `pnpm build:standalone`
3. Stage 3 (`runtime`): Node 24 slim, copy `dist/standalone/`, entrypoint `node dist/standalone/index.js`
**Exit gate:** File exists, valid Dockerfile syntax.
### Task 2 — `Dockerfile.remote` (nginx static image)
**File:** `Dockerfile.remote` (repo root)
Multi-stage build:
1. Stage 1 (`deps`): same as Task 1
2. Stage 2 (`build`): copy source, run `pnpm build:remote`
3. Stage 3 (`runtime`): `nginx:alpine`, copy `dist/remote/` to `/usr/share/nginx/html`, expose port 80
**Exit gate:** File exists, valid Dockerfile syntax.
### Task 3 — Health endpoint (`src/server/routes/health.ts`)
**Files:** `src/server/routes/health.ts`, `src/server/routes/health.test.ts`
Export `healthMiddleware(options)` factory function that returns an Express-style `(req, res, next)` handler. The middleware:
- Pings the upstream API client on each request (with configurable timeout, default 5000ms)
- Tracks last successful ping timestamp
- Returns 200 `{ status: "ok" }` if last success < 60s ago
- Returns 503 `{ status: "degraded", reason: "upstream_unreachable" }` otherwise
**TDD:** Tests mock ApiClient, verify 200/503 responses, verify timeout behavior.
**Exit gate:** `pnpm test` passes health tests.
### Task 4 — Graceful shutdown (`src/server/shutdown.ts`)
**Files:** `src/server/shutdown.ts`, `src/server/shutdown.test.ts`
Export `registerGracefulShutdown(options)` factory function that:
- Registers a SIGTERM handler
- On SIGTERM: calls `server.close()`, waits up to `drainTimeoutMs` (default 30000), flushes logger transport, exits with code 0
- If drain times out, force-exits with code 1
**TDD:** Tests mock `process.on`, `server.close`, `logger` flush; verify shutdown sequence.
**Exit gate:** `pnpm test` passes shutdown tests.
### Task 5 — CI deploy workflow (`.github/workflows/deploy.yml`)
**File:** `.github/workflows/deploy.yml`
Template workflow triggered on push to `main`:
- Checkout, setup Node 24, install pnpm, `pnpm install --frozen-lockfile`
- Build both targets (`pnpm build:both`)
- Build Docker images (`Dockerfile.react`, `Dockerfile.remote`)
- Push to registry (placeholder)
- Deploy to testing environment (placeholder)
**Exit gate:** File exists, valid YAML syntax.
### Task 6 — Operational runbook (`docs/superpowers/phase-1/runbook.md`)
**File:** `docs/superpowers/phase-1/runbook.md`
Covers:
1. Incident response decision tree
2. Canary rollout procedure
3. Rollback procedure (auto + manual)
4. Health-check interpretation
5. Log query cookbook
6. Known-failure playbooks (6 scenarios)
**Exit gate:** File exists, covers all required sections.
---
## Verification
After all tasks:
```bash
pnpm typecheck && pnpm lint && pnpm test
```