Repository navigation
352 lines (339 loc) · 14.2 KB
/
Copy pathci.yml
File metadata and controls
352 lines (339 loc) · 14.2 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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Principle of least privilege: every job in this workflow only reads the
# repo (checkout, build, test, upload-artifact within the workflow run).
# No job pushes, comments, opens PRs, or touches deployments — so the
# default token scope is restricted to `contents: read`. The Codecov
# upload uses `secrets.CODECOV_TOKEN` and does not need GITHUB_TOKEN
# write scopes. Adding a write permission here would expand blast radius
# without enabling any current job. The `e2e` / `e2e-consumer` / `examples` container
# jobs additionally grant `packages: read` at the job level so the runner
# can pull the Playwright image from our GHCR mirror (still read-only).
permissions:
contents: read
# Version Packages PRs (opened by changesets/action on `changeset-release/*`
# branches) intentionally skip the real CI work — every check already ran on
# the feature PR that produced the changeset. To keep branch-protection
# happy, each required job still STARTS (so a status is reported) but its
# steps no-op when the branch matches `changeset-release/*`. The job-level
# env flag `IS_VP` drives that gate; do NOT replace it with a job-level `if:`
# because a skipped job reports no status and required-check rules will
# block the PR forever.
#
# For workflows to fire on the VP PR at all, the changesets/action push must
# be made with a non-`GITHUB_TOKEN` identity — see publish.yml.
jobs:
lint:
runs-on: ubuntu-latest
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR. Original checks ran on the producing PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
uses: actions/cache@v6
with:
path: node_modules/.cache/eslint
key: eslint-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', 'pnpm-lock.yaml') }}-${{ github.sha }}
restore-keys: |
eslint-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', 'pnpm-lock.yaml') }}-
eslint-${{ runner.os }}-
- if: env.IS_VP != 'true'
run: pnpm run format:check
- if: env.IS_VP != 'true'
run: pnpm run lint
- if: env.IS_VP != 'true'
run: pnpm run deps:check
typecheck:
runs-on: ubuntu-latest
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
run: pnpm run typecheck
build:
runs-on: ubuntu-latest
needs: [lint, typecheck]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
run: pnpm run build
# Docs are otherwise never built in CI, and Renovate auto-merges
# devDependency bumps (including vitepress prerelease updates) — this is
# the only gate against a docs-breaking upgrade landing unnoticed.
- if: env.IS_VP != 'true'
run: pnpm --filter @templatical/docs run build
# Guard the editor's published-tarball install footprint. Catches anyone
# adding a runtime `dependency` that re-bloats consumer node_modules or
# re-introduces the duplicate-Vue-instance bug. See packages/editor/scripts/verify-pack.mjs.
- if: env.IS_VP != 'true'
run: pnpm --filter @templatical/editor run verify:pack
# Hard gate: every showcase template must pass the accessibility linter.
# Runs after build because the script imports the built quality dist —
# tsx can't evaluate `import.meta.glob` from source.
- if: env.IS_VP != 'true'
run: pnpm --filter @templatical/playground run lint:templates
# The playground's production build (what play.templatical.com serves) is
# otherwise only built by the deploy, and e2e runs `vite dev`. It compiles
# the editor from source, so this is where its own CSS could reach the
# editor's shadow root. See apps/playground/scripts/verify-shadow-sheet.mjs.
- if: env.IS_VP != 'true'
run: pnpm --filter @templatical/playground run build
- if: env.IS_VP != 'true'
run: pnpm --filter @templatical/playground run verify:shadow-sheet
- if: env.IS_VP != 'true'
uses: actions/upload-artifact@v7
with:
name: dist
path: packages/*/dist
retention-days: 1
include-hidden-files: true
test:
runs-on: ubuntu-latest
needs: [build]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
uses: actions/download-artifact@v8
with:
name: dist
path: packages/
- if: env.IS_VP != 'true'
run: pnpm run test:coverage
- if: env.IS_VP != 'true'
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: .
# The CLI can report a queued upload while Codecov never processes
# it (deactivated repo, rejected token). The README badge then sits
# on a stale percentage with a green test job. Failing here is the
# signal that the badge has gone stale.
fail_ci_if_error: true
e2e:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
# Playwright's official image ships Chromium + every system dep
# preinstalled, skipping the ~5 min `playwright install-deps` step that
# fresh ubuntu-latest runners would otherwise pay every run (apt state
# isn't cacheable across runs).
#
# Pulled from our GHCR mirror, NOT mcr.microsoft.com directly: MCR's Azure
# edge intermittently throttles concurrent pulls ("The request is
# blocked."), which fails "Initialize containers" and turns this job red at
# random. GHCR is co-located with the runners and isn't throttled. The
# mirror is kept in sync by .github/workflows/mirror-playwright.yml. The tag
# must match the pinned `@playwright/test` version in pnpm-lock.yaml AND the
# tag that workflow mirrors.
container:
image: ghcr.io/templatical/playwright:v1.64.1-jammy
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
needs: [build]
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
uses: actions/download-artifact@v8
with:
name: dist
path: packages/
- if: env.IS_VP != 'true'
run: pnpm exec playwright test --shard=${{ matrix.shard }}/3
- if: env.IS_VP != 'true' && !cancelled()
uses: actions/upload-artifact@v7
with:
name: blob-report-${{ matrix.shard }}
path: blob-report/
retention-days: 1
e2e-consumer:
# Smoke-test the editor as a real consumer would: pack, install into a
# fresh project, mount, click. Catches the duplicate-Vue-instance class of
# bug that the workspace-resolved playground e2e cannot see.
#
# Skipped on Version Packages PRs (changeset-release/*) because the packed
# tarballs reference the about-to-be-released workspace versions, which
# aren't on npm yet — `npm install` would fail with ETARGET. The original
# gate already ran on the producing PR; the post-merge run on `main` (after
# publish) re-covers the surface.
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
# Same rationale as the `e2e` job — the Playwright image skips the
# system-dep install step, pulled from our GHCR mirror to dodge MCR's
# intermittent edge throttling (see the `e2e` job + mirror-playwright.yml).
container:
image: ghcr.io/templatical/playwright:v1.64.1-jammy
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
needs: [build]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR. Tarballs reference unreleased versions."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
# No dist download: the prep script builds what it packs, and every
# workspace package those builds read from dist/, from this checkout
# (`buildOrder` in packages/editor/scripts/consumer-fixture.mjs). A
# downloaded dist would hide a missing build edge until a fresh local
# checkout hit it.
- if: env.IS_VP != 'true'
run: pnpm run test:e2e:consumer
- if: env.IS_VP != 'true' && failure()
uses: actions/upload-artifact@v7
with:
name: e2e-consumer-report
path: playwright-report/
retention-days: 7
webpack-consumer:
# Build the editor as a real Webpack 5 consumer would. Catches issue #63:
# if anyone adds a dynamic `import()` to an optional peer without try/catch,
# webpack escalates "Module not found" from warning to error and breaks the
# consumer's production build. See packages/editor/scripts/webpack-consumer-verify.mjs.
runs-on: ubuntu-latest
needs: [build]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
# No dist download, for the same reason as e2e-consumer.
- if: env.IS_VP != 'true'
run: pnpm run test:webpack-consumer
examples:
# Build and run each examples/<name> app the way a new user would, against
# this PR's packed packages: materialize it with every @templatical
# dependency pointed at a tarball, npm install, production build, typecheck,
# start, then the Playwright smoke spec. The nextjs build is Next.js 16's
# Turbopack build, which fails on a UMD/AMD define() wrapper in the editor
# bundle with `error TP1200` (issue #67).
# See packages/editor/scripts/examples-verify.mjs.
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: read
# Same image and mirror as the `e2e` job.
container:
image: ghcr.io/templatical/playwright:v1.64.1-jammy
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
needs: [build]
strategy:
fail-fast: false
matrix:
example: [nextjs, nuxt, sveltekit, react-router, react-vite]
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
# The packages are packed from the build job's dist, not rebuilt.
- if: env.IS_VP != 'true'
uses: actions/download-artifact@v8
with:
name: dist
path: packages/
- if: env.IS_VP != 'true'
run: pnpm run test:examples ${{ matrix.example }}
- if: env.IS_VP != 'true' && failure()
uses: actions/upload-artifact@v7
with:
name: examples-report-${{ matrix.example }}
path: playwright-report/
retention-days: 7
e2e-report:
runs-on: ubuntu-latest
needs: [e2e]
if: ${{ !cancelled() && needs.e2e.result != 'skipped' }}
env:
IS_VP: ${{ startsWith(github.head_ref, 'changeset-release/') || startsWith(github.ref_name, 'changeset-release/') }}
steps:
- name: Skip on Version Packages PR
if: env.IS_VP == 'true'
run: echo "Skipped — Version Packages PR. No e2e artifacts produced."
- if: env.IS_VP != 'true'
uses: actions/checkout@v7
- if: env.IS_VP != 'true'
uses: ./.github/actions/setup
- if: env.IS_VP != 'true'
uses: actions/download-artifact@v8
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- if: env.IS_VP != 'true'
run: pnpm exec playwright merge-reports --reporter html ./all-blob-reports
- if: env.IS_VP != 'true'
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report/
retention-days: 7