|
| 1 | +# Release Ship And OTA Implementation Plan |
| 2 | + |
| 3 | +> **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. |
| 4 | +
|
| 5 | +**Goal:** Add a single release command that prepares and ships a release end-to-end, then verifies the published desktop OTA assets before reporting success. |
| 6 | + |
| 7 | +**Architecture:** Keep existing release preparation and validation scripts as building blocks, then add a thin orchestration script that runs preflight, drives the tag push path, waits for the GitHub Actions release workflow, and verifies the live GitHub Release contents through `gh`. Tighten the GitHub Actions release workflow so stable desktop publishing always includes both macOS architectures and a merged updater manifest, with asset validation failing closed if OTA coverage is incomplete. |
| 8 | + |
| 9 | +**Tech Stack:** Node.js scripts, GitHub CLI, GitHub Actions YAML, Vitest, Bun workspace scripts |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +### Task 1: Add the release shipping orchestrator |
| 14 | + |
| 15 | +**Files:** |
| 16 | + |
| 17 | +- Create: `scripts/release-ship.ts` |
| 18 | +- Modify: `package.json` |
| 19 | +- Test: `scripts/release-ship.test.ts` |
| 20 | + |
| 21 | +- [ ] **Step 1: Write the failing tests for release shipping orchestration** |
| 22 | + |
| 23 | +```ts |
| 24 | +describe("release-ship", () => { |
| 25 | + it("runs validation, preparation, waits for release workflow, and verifies OTA assets", () => { |
| 26 | + // Assert the command order and that the verifier checks the live release. |
| 27 | + }); |
| 28 | + |
| 29 | + it("fails when the published release is missing required OTA coverage", () => { |
| 30 | + // Assert missing x64 mac assets or merged manifest causes a throw. |
| 31 | + }); |
| 32 | +}); |
| 33 | +``` |
| 34 | + |
| 35 | +- [ ] **Step 2: Run the script tests to verify the new cases fail** |
| 36 | + |
| 37 | +Run: `bun run --cwd scripts test` |
| 38 | +Expected: FAIL with missing `scripts/release-ship.ts` exports or missing test expectations. |
| 39 | + |
| 40 | +- [ ] **Step 3: Implement `scripts/release-ship.ts` with explicit phases** |
| 41 | + |
| 42 | +```ts |
| 43 | +run("node", ["scripts/pre-release-validate.ts", version, "--ci"]); |
| 44 | +run("node", ["scripts/prepare-release.ts", version, "--skip-checks"]); |
| 45 | +const runId = await waitForWorkflowRun({ workflow: "release.yml", tag: `v${version}` }); |
| 46 | +await watchWorkflowRun(runId); |
| 47 | +await verifyPublishedReleaseAssets({ tag: `v${version}` }); |
| 48 | +``` |
| 49 | + |
| 50 | +- [ ] **Step 4: Add a single package entry point** |
| 51 | + |
| 52 | +```json |
| 53 | +"release:ship": "node scripts/release-ship.ts" |
| 54 | +``` |
| 55 | + |
| 56 | +- [ ] **Step 5: Re-run the script tests** |
| 57 | + |
| 58 | +Run: `bun run --cwd scripts test` |
| 59 | +Expected: PASS for the new `release-ship` tests. |
| 60 | + |
| 61 | +### Task 2: Make stable desktop publishing fail closed on OTA completeness |
| 62 | + |
| 63 | +**Files:** |
| 64 | + |
| 65 | +- Modify: `.github/workflows/release.yml` |
| 66 | +- Modify: `scripts/validate-release-assets.ts` |
| 67 | +- Test: `scripts/validate-release-assets.test.ts` |
| 68 | +- Test: `scripts/release-smoke.ts` |
| 69 | + |
| 70 | +- [ ] **Step 1: Write the failing asset validation tests** |
| 71 | + |
| 72 | +```ts |
| 73 | +it("requires both macOS arm64 and x64 desktop payloads for coordinated releases", () => { |
| 74 | + assert.throws(() => validateReleaseAssets([...missingX64]), /macOS x64 DMG/); |
| 75 | +}); |
| 76 | + |
| 77 | +it("requires a merged latest-mac.yml manifest alongside the dual-arch payloads", () => { |
| 78 | + assert.throws(() => validateReleaseAssets([...missingManifest]), /macOS updater manifest/); |
| 79 | +}); |
| 80 | +``` |
| 81 | + |
| 82 | +- [ ] **Step 2: Run the script tests to verify the new asset expectations fail** |
| 83 | + |
| 84 | +Run: `bun run --cwd scripts test` |
| 85 | +Expected: FAIL because `validateReleaseAssets` does not yet require dual-arch mac assets. |
| 86 | + |
| 87 | +- [ ] **Step 3: Tighten release asset validation** |
| 88 | + |
| 89 | +```ts |
| 90 | +{ |
| 91 | + label: "macOS arm64 DMG", |
| 92 | + matches: (assetName) => assetName.endsWith("-arm64.dmg"), |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +- [ ] **Step 4: Update `release.yml` to build both macOS architectures and merge manifests before publish** |
| 97 | + |
| 98 | +```yml |
| 99 | +- label: macOS arm64 |
| 100 | + runner: macos-14 |
| 101 | + platform: mac |
| 102 | + arch: arm64 |
| 103 | +- label: macOS x64 |
| 104 | + runner: macos-13 |
| 105 | + platform: mac |
| 106 | + arch: x64 |
| 107 | +``` |
| 108 | +
|
| 109 | +```yml |
| 110 | +- name: Merge macOS update manifests |
| 111 | + run: node scripts/merge-mac-update-manifests.ts release-assets/latest-mac.yml release-assets/latest-mac-x64.yml release-assets/latest-mac.yml |
| 112 | +``` |
| 113 | +
|
| 114 | +- [ ] **Step 5: Update release smoke fixtures to model the new dual-arch mac release contract** |
| 115 | +
|
| 116 | +```ts |
| 117 | +writeReleaseAssetFixtures([ |
| 118 | + "OK-Code-9.9.9-smoke.0-arm64.dmg", |
| 119 | + "OK-Code-9.9.9-smoke.0-x64.dmg", |
| 120 | + "OK-Code-9.9.9-smoke.0-arm64.zip", |
| 121 | + "OK-Code-9.9.9-smoke.0-x64.zip", |
| 122 | +]); |
| 123 | +``` |
| 124 | + |
| 125 | +- [ ] **Step 6: Re-run the script tests** |
| 126 | + |
| 127 | +Run: `bun run --cwd scripts test` |
| 128 | +Expected: PASS for updated asset validation and smoke coverage. |
| 129 | + |
| 130 | +### Task 3: Verify the live published release contract |
| 131 | + |
| 132 | +**Files:** |
| 133 | + |
| 134 | +- Modify: `scripts/release-ship.ts` |
| 135 | +- Test: `scripts/release-ship.test.ts` |
| 136 | +- Modify: `docs/release.md` |
| 137 | + |
| 138 | +- [ ] **Step 1: Add failing tests for published release inspection** |
| 139 | + |
| 140 | +```ts |
| 141 | +it("checks GitHub Release assets for dual-arch mac OTA payloads after workflow success", () => { |
| 142 | + // Assert `gh release view` or `gh api` output is parsed and validated. |
| 143 | +}); |
| 144 | +``` |
| 145 | + |
| 146 | +- [ ] **Step 2: Run the script tests to verify the published-release checks fail** |
| 147 | + |
| 148 | +Run: `bun run --cwd scripts test` |
| 149 | +Expected: FAIL until `release-ship.ts` validates live release assets. |
| 150 | + |
| 151 | +- [ ] **Step 3: Implement release verification and operator-facing docs** |
| 152 | + |
| 153 | +```ts |
| 154 | +const assets = await listReleaseAssets(tag); |
| 155 | +validateReleaseAssets(assets); |
| 156 | +validateMergedMacManifest(await downloadReleaseAsset(tag, "latest-mac.yml")); |
| 157 | +``` |
| 158 | + |
| 159 | +```md |
| 160 | +Run `bun run release:ship <version>` to perform local preflight, push the tag, wait for `release.yml`, and verify OTA assets on the published GitHub Release. |
| 161 | +``` |
| 162 | + |
| 163 | +- [ ] **Step 4: Re-run script tests** |
| 164 | + |
| 165 | +Run: `bun run --cwd scripts test` |
| 166 | +Expected: PASS for release shipping orchestration and live-release verification logic. |
| 167 | + |
| 168 | +### Task 4: Workspace verification |
| 169 | + |
| 170 | +**Files:** |
| 171 | + |
| 172 | +- Modify: `package.json` |
| 173 | +- Modify: `scripts/package.json` |
| 174 | +- Modify: `.github/workflows/release.yml` |
| 175 | +- Modify: `docs/release.md` |
| 176 | +- Modify: `scripts/release-smoke.ts` |
| 177 | +- Modify: `scripts/validate-release-assets.ts` |
| 178 | +- Modify: `scripts/validate-release-assets.test.ts` |
| 179 | +- Create: `scripts/release-ship.ts` |
| 180 | +- Create: `scripts/release-ship.test.ts` |
| 181 | + |
| 182 | +- [ ] **Step 1: Run formatting** |
| 183 | + |
| 184 | +Run: `bun run fmt` |
| 185 | +Expected: exit 0 |
| 186 | + |
| 187 | +- [ ] **Step 2: Run lint** |
| 188 | + |
| 189 | +Run: `bun run lint` |
| 190 | +Expected: exit 0 |
| 191 | + |
| 192 | +- [ ] **Step 3: Run typecheck** |
| 193 | + |
| 194 | +Run: `bun run typecheck` |
| 195 | +Expected: exit 0 |
| 196 | + |
| 197 | +- [ ] **Step 4: Run focused script tests and release smoke** |
| 198 | + |
| 199 | +Run: `bun run --cwd scripts test` |
| 200 | +Expected: PASS |
| 201 | + |
| 202 | +Run: `bun run release:smoke` |
| 203 | +Expected: PASS |
| 204 | + |
| 205 | +- [ ] **Step 5: Run the full required workspace verification** |
| 206 | + |
| 207 | +Run: `bun run fmt && bun run lint && bun run typecheck` |
| 208 | +Expected: all exit 0 |
0 commit comments