# OpenLife CLI v1.19.0 — Template Smoke CI + Deploy Promote

**Released:** 2026-05-22
**Theme:** Regression-killer CI for templates + multi-environment deploy promote.

---

## Why this release

v1.18.2 hotfix shipped 5 template bugs (Stripe apiVersion, Clerk middleware
regex, Next.js 16 route group collision, ClerkProvider SSG breakage, etc.)
because the unit tests validated the **ScaffoldEngine** (the producer) but
never instantiated the **scaffolded output** (the artifact).

v1.19.0 closes that gap and adds a missing piece of the deploy story:
promoting an existing deployment to a target environment instead of
running a fresh build.

---

## What's new

### 1. Template smoke CI workflow

New file: `.github/workflows/template-smoke.yml`.

For each template under `templates/`, CI now runs the **end-to-end path**:

```
scaffold → install → build
```

If `npm run build` on the scaffolded project fails for any template, the
PR is blocked. Adding a new template requires only appending it to the
matrix in the workflow file.

This prevents v1.18.2-class regressions before they reach npm.

### 2. `openlife deploy promote <env>`

New surface for promoting an existing deployment to a target environment:

```bash
openlife deploy promote prod
openlife deploy promote prod --from <preview-deployment-url>
openlife deploy promote staging --provider railway
```

**Hybrid model — each adapter uses its native promote strategy:**

| Provider | Strategy | Notes |
|---|---|---|
| **Vercel** | Alias swap | `vercel promote <id>` — atomic, zero-downtime. `--from` is optional (Vercel picks latest preview if omitted). |
| **Cloudflare** | Versioned alias swap | `wrangler versions deploy <id>@100%` when `--from` provided; otherwise `wrangler deploy --env <name>`. |
| **Railway** | Redeploy with target env | `railway up --environment <env> --ci`. No alias concept on Railway. |
| **Fly** | Redeploy target app | `fly deploy --app <name>`. Convention: one Fly app per environment. |

### 3. `DeployAdapter.promote()`

New required method on the `DeployAdapter` interface:

```typescript
interface DeployAdapter {
  // existing: detect, preflight, deploy, rollback
  promote(opts: PromoteOptions): Promise<DeployResult>;
}

interface PromoteOptions {
  to: string;              // target env (required)
  from?: string;           // source deployment (alias-swap providers)
  domain?: string;         // target domain (alias-swap providers)
  cwd?: string;
  timeoutMs?: number;
}
```

---

## Files added

- `.github/workflows/template-smoke.yml` — scaffold → install → build per template
- `docs/v1.19-changelog.md` — this file

## Files modified

- `src/orchestrator/deploy/DeployAdapter.ts` — `PromoteOptions` type + `promote()` on interface
- `src/orchestrator/deploy/VercelAdapter.ts` — alias-swap via `vercel promote`
- `src/orchestrator/deploy/RailwayAdapter.ts` — redeploy via `railway up --environment`
- `src/orchestrator/deploy/CloudflareAdapter.ts` — versioned alias-swap via `wrangler versions deploy`
- `src/orchestrator/deploy/FlyAdapter.ts` — redeploy via `fly deploy --app`
- `src/cli/DeployCommand.ts` — `promote <env>` subcommand
- `src/test_deploy_adapters.ts` — +4 scenarios (12–15) for promote contract per adapter
- `package.json` — version 1.18.3 → 1.19.0, files[] +1

---

## Migration notes

**For users of the public CLI:** no breaking changes. New subcommand only.

**For maintainers implementing custom `DeployAdapter`s outside this
repo:** `promote()` is now required. Add an implementation (alias-swap or
redeploy) following the patterns in the bundled adapters.

---

## Verification

```bash
npm install
npm run build
npm run test:deploy-adapters   # all 15 scenarios pass
npm run test:all               # full suite green

# CLI surface check
node bin/openlife.js deploy --help
node bin/openlife.js deploy promote --help
```

CI: `.github/workflows/template-smoke.yml` runs on every push to main and
every PR — surface visible at github.com/GOOODZ/openlife-core/actions.
