-
Notifications
You must be signed in to change notification settings - Fork 3.5k
146 lines (141 loc) · 6.26 KB
/
Copy pathweb.yml
File metadata and controls
146 lines (141 loc) · 6.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
name: Web Frontend
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
workflow_dispatch:
permissions:
contents: read
jobs:
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install dependencies
run: npm ci
- name: Check facts drift
# facts.generated.ts is TRACKED (committed), so verify the committed
# copy matches the workspace BEFORE regenerating. Running prebuild first
# would self-heal the working tree and let a stale committed file pass
# (#3771). check:facts ignores the volatile generatedAt/latestRelease
# fields by design, so it is safe to run against the committed copy that
# exists at checkout.
run: npm run check:facts
- name: Check published-release fact is current
# web/data/latest-published-release.json is hand-maintained and feeds
# facts.generated.ts. Nothing wrote it, so it drifted to v0.9.10 while
# v0.9.11 was live, and the post-deploy comparison below failed on
# latestPublishedRelease.tag AFTER the site had already shipped. Gate it
# here so the mismatch is caught before deploying, not after.
env:
GITHUB_TOKEN: ${{ github.token }}
run: npm run check:latest-release
- name: Generate derived facts
# Regenerate after the drift gate so tsc --noEmit (TS2307 without it) and
# the build use a current facts.generated.ts. When the gate passes this
# only refreshes the generatedAt timestamp.
run: npm run prebuild
- name: Check docs parity
# Fails CI when docs-map.ts references non-existent repo files or
# when website version / command snippets are stale.
run: npm run check:docs
- name: Check design tokens
# app/tokens.css is generated from crates/tui/src/palette/tokens.rs.
# Fails CI when the palette moved and the export was not re-run.
run: npm run check:tokens
- name: Run tests
run: npm test
- name: Run ESLint
run: npm run lint
- name: TypeScript type check
run: npx tsc --noEmit
- name: Build production site
run: npm run build
deploy-reminder:
name: Deployment approval needed
runs-on: ubuntu-latest
needs: lint
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Surface the manual deployment gate
env:
REVISION: ${{ github.sha }}
run: |
echo "::notice title=Web deployment approval needed::Revision ${REVISION} passed the web gates but is not deployed. Dispatch web.yml on main to publish it."
{
echo "## Web deployment approval needed"
echo
echo "Revision \`${REVISION}\` passed the web gates but has **not** been deployed."
echo
echo "A maintainer can publish it with \`gh workflow run web.yml --repo Hmbown/CodeWhale --ref main\`."
} >> "$GITHUB_STEP_SUMMARY"
deploy:
name: Deploy to Cloudflare
runs-on: ubuntu-latest
needs: lint
# Deploy is MANUAL ONLY: a human dispatches this workflow on main. Pushes
# and pull requests still run `lint` above, but they never reach Cloudflare.
# This mirrors scripts/check-cloudflare-deploy-env.mjs, which fails closed
# unless GITHUB_EVENT_NAME is workflow_dispatch, GITHUB_REF is
# refs/heads/main, and GITHUB_SHA is an exact 40-hex revision — a push
# trigger here would only produce a red job after `lint` had already run.
# lib/deploy-preflight.test.ts asserts both halves of that contract.
# `needs: lint` is the gate: facts drift, docs parity, tests, ESLint, tsc,
# and a production build all pass before anything reaches Cloudflare.
if: >-
github.event_name == 'workflow_dispatch'
&& github.ref == 'refs/heads/main'
# Serialize deploys so two dispatches landing close together cannot race and
# leave Cloudflare serving the older bundle. Never cancel in progress: a
# half-finished OpenNext upload is worse than a queued one.
concurrency:
group: deploy-codewhale-web
cancel-in-progress: false
defaults:
run:
working-directory: web
env:
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
steps:
- uses: actions/checkout@v7
# Pin the checkout to the exact revision this dispatch resolved, so the
# SHA reported to compare:deployed-facts and asserted on the public
# receipt below is the SHA that was actually built, even if main moves
# while the run is queued behind the concurrency group.
with:
ref: ${{ github.sha }}
- uses: actions/setup-node@v7
with:
node-version: 22
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install dependencies
run: npm ci
- name: Record deployed/source drift
# Read-only and credential-free. A mismatch is the normal state here —
# it is the gap this run is about to close — so this step reports
# without gating. The real assertion is the post-deploy verification
# below, which must observe this exact revision on the public receipt.
run: npm run compare:deployed-facts -- --expected-revision "$GITHUB_SHA"
- name: Check Cloudflare deploy environment
run: npm run check:deploy-env
# npm's deploy script performs one OpenNext build, then deploys that exact
# bundle. Wrangler must not run a custom post-cache build: OpenNext
# populates the remote cache before it hands the bundle to Wrangler.
- name: Build and deploy exact OpenNext bundle
run: npm run deploy
- name: Verify exact deployed revision
# The public /api/facts receipt must identify this workflow's exact
# checkout before the manual deployment run can finish green.
run: npm run check:deployed-facts -- --expected-revision "$GITHUB_SHA" --attempts 10 --retry-delay-ms 3000