-
Notifications
You must be signed in to change notification settings - Fork 0
205 lines (192 loc) · 9.91 KB
/
Copy pathci.yml
File metadata and controls
205 lines (192 loc) · 9.91 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:
jobs:
# The four Node floor declarations (`engines.node` in the root, in `apps/docs`
# and in `tools/ci-scripts`, plus `.node-version`) are read by nothing in the
# install path: `.npmrc` sets no `engine-strict`, pnpm does not enforce
# `engines` by default, and every workflow here pins `node-version`
# explicitly instead of consulting them. This job is what makes them
# mechanically checkable. It needs no install — the script is zero-dependency
# and reads the lockfile as text — so it stays a seconds-long job that can
# run alongside `build`.
node-floor:
name: Node floor
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
# 裁决 (PR #74): a validator observed only green is indistinguishable
# from one that cannot go red. The fixtures run before the real scan, so
# a rule that stopped being able to fail fails the job on its own.
- name: Self-test
shell: bash
run: node .github/scripts/check-node-floor.mjs --self-test
# `shell: bash` is load-bearing here, not tidiness. The DEFAULT shell for
# a `run:` step is `bash -e {0}` with no pipefail, so in `node ... | tee`
# the step takes tee's exit status and a gate that exits 1 passes the job
# silently. Naming the shell gets `bash --noprofile --norc -eo pipefail
# {0}`, which propagates it.
- name: Check
shell: bash
run: node .github/scripts/check-node-floor.mjs | tee -a "$GITHUB_STEP_SUMMARY"
build:
runs-on: ubuntu-latest
# `NEXT_PRIVATE_STANDALONE` is what `@opennextjs/aws` sets before it runs
# `next build` — its own comment reads "Equivalent to setting `output:
# "standalone"` in next.config.js". Without it a plain `next build`
# produces no `.next/standalone/`, and the packaging step below fails on a
# missing `pages-manifest.json` three directories inside it. Measured on
# this branch before it was set: `ENOENT ... .next/standalone/apps/docs/
# .next/server/pages-manifest.json`.
#
# Set for the whole job rather than for the deploy path only, so that what
# a pull request builds is the same shape as what gets published. A build
# that differs from the deploy build is a small instance of the defect this
# card is about.
#
# It has to be declared in `turbo.json` as well: turbo 2 runs tasks in
# strict env mode, so an undeclared variable never reaches `next build` —
# and declaring it is also what puts it in the cache key, so a `.next`
# cached from before this line cannot be replayed without the standalone
# tree the packaging step needs.
env:
NEXT_PRIVATE_STANDALONE: 'true'
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
# `scripts/pm/check-half-states.mjs` is a verbatim upstream copy (#237)
# whose 1551 cases were run by nothing here. It is a STEP and not a
# registry entry because `tools/ci-scripts/run-self-tests.mjs` scans
# `.github/scripts` top level only, and that is load-bearing — it is what
# lets `.github/scripts/lib/` exist without tripping the
# unregistered-self-test rule. Widening the scan to reach one script would
# change this repo's gate topology; a step changes nothing.
#
# What it buys is a drift detector, not a hash-pin: #237's ablation
# mutated `DEFAULT_SWEEP_REPO` in this copy and 2 of the 1551 went red, so
# an edit here that changes BEHAVIOUR fails. One that changes bytes
# without changing behaviour still passes — pinning this copy to upstream
# byte for byte needs a cross-repo credential and is a separate decision.
#
# `--self-test` is the whole of it. Without the flag the script runs a
# live sweep needing a transport prerequisite this job does not have; that
# caller is `.github/workflows/half-state-patrol.yml`, on its own
# schedule. Zero-dependency and about a second, so it runs before the
# build rather than behind it.
- name: Half-state sweeper self-test
shell: bash
run: node scripts/pm/check-half-states.mjs --self-test
# `content/docs/**/*.zh-Hant.mdx` and `meta.zh-Hant.json` are generated
# from the Simplified siblings by `apps/docs/scripts/gen-zh-hant.mjs` and
# committed, because `lib/seo.ts` tells a real translation from an English
# fallback by the presence of a locale-suffixed FILE — a conversion done
# while rendering would leave the locale out of every sitemap entry and
# hreflang cluster. Committed output needs a gate or it drifts: this
# regenerates in memory and compares bytes, so a hand edit, a stale file
# whose source was retired, and a converter upgrade nobody re-ran all fail
# here with the same one-line fix.
#
# Before `type-check` on purpose: it needs no build, and a content drift
# reported as a type error is a wrong first diagnosis.
- name: Generated zh-Hant is current
run: node apps/docs/scripts/gen-zh-hant.mjs --check
- run: pnpm turbo run type-check --continue
- run: pnpm turbo run build
# Reads the BUILT sitemap and asserts its locale composition against an
# oracle derived from `content/docs/`. It has to come after `build` — the
# measurement is taken off the artifact, because importing `sitemap.ts`
# pulls in the whole MDX collection and the `@/` alias.
#
# Not a turbo task on purpose: turbo would hash it against the ci-scripts
# package's own inputs, which do not include `apps/docs/.next/`, so a
# locale regression would replay a cached green. `pnpm turbo run test`
# below still runs this script's `--self-test`, which is what keeps its
# rules provably able to fail.
#
# `shell: bash` is load-bearing, not tidiness. The DEFAULT shell for a
# `run:` step is `bash -e {0}` with no pipefail, so in `node ... | tee`
# the step takes tee's exit status and a gate that exits 1 passes the job
# silently. Naming the shell gets `bash --noprofile --norc -eo pipefail
# {0}`, which propagates it.
- name: Locale surface
shell: bash
run: node .github/scripts/check-locale-surface.mjs | tee -a "$GITHUB_STEP_SUMMARY"
- run: pnpm turbo run test
# Defect 2 of #269: the deploy used to run its own `pnpm install` and its
# own `opennextjs-cloudflare build`, so CI built the site, threw it away,
# and the deploy published a SECOND build that nothing here had checked.
# The published artifact was unverified by construction.
#
# `--skipNextBuild` packages the `.next` output `pnpm turbo run build`
# produced above — the same output `Locale surface` measured and the same
# tree every step in this job passed — and `deploy-docs.yml` uploads THIS
# bundle rather than making another one.
#
# Last in the job on purpose: the artifact then only exists for a commit
# that cleared every gate above it. And only on a push to `main`, because
# that is the only event that deploys, so a pull request pays nothing.
- name: Package the Worker from the build this job tested
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
working-directory: apps/docs
run: pnpm exec opennextjs-cloudflare build --skipNextBuild
# `include-hidden-files` is load-bearing, not tidiness: the compiled
# OpenNext config the deploy reads lives at `.open-next/.build/`, and
# upload-artifact excludes dotted paths by default. Without it the
# download succeeds, the deploy exits 1 on a missing config, and the
# cause is three directories away from the message.
- name: Upload the Worker bundle
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v7
with:
name: docs-worker
path: apps/docs/.open-next
include-hidden-files: true
if-no-files-found: error
retention-days: 3
# Defect 1 of #269: `deploy-docs.yml` used to hang off `push: branches:
# [main]` exactly as this workflow does, so the two ran in PARALLEL and a
# commit that failed any gate above still deployed. There was no `needs:` and
# no `workflow_run` anywhere.
#
# As a job here it cannot start until `node-floor` and `build` are green, and
# the `if:` keeps it off pull requests and merge groups. `workflow_run` would
# also have gated it, but it fires on a FAILED run too — the conclusion has
# to be re-checked by hand inside the workflow — and it runs detached from
# the run whose artifact it publishes, which is what the `with:` line here
# depends on.
#
# ⚠️ This deploy is expected to FAIL while #261 is open: `main` builds a
# Worker over Cloudflare's 64 MiB limit, so the upload is rejected at version
# creation and the serving version cannot be displaced. That is the current
# deliberate steady state, not a regression from this wiring.
deploy-docs:
name: Deploy docs
needs: [node-floor, build]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
# Preserves what `deploy-docs.yml` declared for itself before it became a
# called workflow: one deploy at a time, and never cancel one in flight.
concurrency:
group: deploy-docs
cancel-in-progress: false
permissions:
contents: read
actions: write # dispatch rollback-docs.yml when the smoke check fails
issues: write # file or update the one deploy-failure card
uses: ./.github/workflows/deploy-docs.yml
with:
artifact_name: docs-worker
secrets: inherit