-
Notifications
You must be signed in to change notification settings - Fork 2
209 lines (196 loc) · 9.3 KB
/
Copy pathexamples.yml
File metadata and controls
209 lines (196 loc) · 9.3 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
name: examples
# Two jobs over `examples/`, answering two different questions.
#
# frontend — do the eight bundled example frontends still install and
# build? (#903)
# snippets — do the standalone examples still *run*? (#545)
#
# Installs and builds the eight bundled example frontends (#903).
#
# The two React legs were added by #540. `tsconfig.dev.json` excludes
# every `examples/*/frontend-*` directory — their dependencies come from
# the per-directory `npm ci` below and are unresolvable from the root
# install — and React was the only pair this matrix did not already
# cover, so without them those two directories would have had no check
# of any kind.
#
# These directories had no CI coverage at all until now: no workflow
# carried `examples/**` in a path filter, so a PR touching only the
# examples produced *zero* checks. #890 (a six-directory dependency
# change) is the worked example — it reported an empty check rollup.
#
# What that cost: `ts-pattern` was declared in all six example
# `package.json` files and missing from all six `package-lock.json`
# files, so `npm ci` failed in every example frontend on `develop` and
# nothing noticed, because nothing installs them. Both failure modes
# this job checks are ones that actually happened:
#
# - `npm ci` — manifest/lockfile desync (the defect above).
# Deliberately `ci`, not `install`: only `ci`
# treats a desynced lockfile as an error rather
# than quietly rewriting it.
# - `npm run build` — a dependency bump that breaks the build.
# - `bun install --frozen-lockfile`
# — a `bun.lock` that contradicts the
# `package.json` beside it. These directories
# carry two lockfiles and only the npm one was
# ever installed here. #1402.
#
# There is no committed bundle here to check for staleness any more: the
# output under `examples/*/static/{angular,next,react,svelte}/` is gitignored
# (#559), so what this job builds from source is the only copy that exists.
# The measurement that used to justify not diffing it is what made committing
# it untenable in the first place, and is kept because it is the evidence.
# Two builds from an identical tree were compared:
#
# - Next — a fresh build id every run (`Kb3j7R17-emTNVLO8IAvl` ->
# `BcT-Fi9UF2LGQStSayjG0`), which renames the build-id
# directory and rewrites every RSC payload and HTML entry.
# - Svelte — `_app/version.json` is a build timestamp, and
# `index.html` embeds it.
# - Angular — byte-identical, the only deterministic one.
#
# So a rebuild-and-diff gate would have been permanently red on four legs
# regardless of staleness, and keeping the committed copies honest would have
# needed a content-independent freshness gate — a source hash over the inputs,
# the way `UiAssets.ts` has one. Not committing them dissolves the question
# rather than answering it, which is why no such gate was ever built.
#
# `static/plain/` and `static/lit/` stay committed and need no gate: they have
# no build step, so those files are source, not output.
# `src/**` is in the filter because of the `snippets` job, and it is the
# whole point of it: the failure this gate exists to catch is a framework
# change that breaks an example, and an `examples/**`-only filter — which
# is all `frontend` ever needed — would produce zero checks for exactly
# that case. The `frontend` job pays for the wider trigger by running on
# `src/**` pushes that cannot affect it; eight cached `npm ci` legs are
# cheaper than the alternative of two workflows that drift apart.
on:
push:
branches: ['**']
paths:
- 'src/**'
- 'examples/**'
- 'tests/examples/**'
- 'package.json'
- '.bun-version'
- 'bun.lock'
- '.github/workflows/examples.yml'
pull_request:
branches: [main, develop]
paths:
- 'src/**'
- 'examples/**'
- 'tests/examples/**'
- 'package.json'
- '.bun-version'
- 'bun.lock'
- '.github/workflows/examples.yml'
workflow_dispatch:
# Least privilege, stated rather than inherited: the repository default is
# read-only today, but a settings flip would otherwise hand a write token to
# a job that installs and runs the whole dependency tree. #621
permissions:
contents: read
jobs:
frontend:
runs-on: ubuntu-latest
strategy:
# fail-fast: false so an Angular-only regression doesn't mask a
# Next-only one. Each frontend gets its own red mark — the same
# reasoning as the runtime matrix in multi-runtime.yml.
fail-fast: false
matrix:
# Listed explicitly rather than globbed: a new example frontend
# should be a deliberate line in this file, not a directory that
# silently joins (or silently doesn't) the matrix.
directory:
- examples/chat/frontend-angular
- examples/chat/frontend-next
- examples/chat/frontend-react
- examples/chat/frontend-svelte
- examples/voice/frontend-angular
- examples/voice/frontend-next
- examples/voice/frontend-react
- examples/voice/frontend-svelte
name: ${{ matrix.directory }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# The bundled frontends carry two lockfiles: the `package-lock.json`
# the `npm ci` below installs, and a `bun.lock` that nothing in CI
# ever read. #1401 is what that cost — Dependabot's npm updater
# patched `fast-uri` in both Angular `package-lock.json` files and
# left `bun.lock` on the vulnerable resolution, with no check
# anywhere able to see it. #1402.
#
# Before the Node setup deliberately: a desynced lockfile is cheaper
# to report than an install and a build.
#
# Scope, measured rather than assumed. This catches a `bun.lock`
# that contradicts its own manifest — the state this repo was in:
# both Angular `package.json` files declare `overrides` pinning
# @hono/node-server to ^2.1.0 while `bun.lock` still resolved
# 1.19.14, which exits 1 with `overrides in package.json changed`.
# It does NOT catch a transitive dependency that is merely stale:
# reverting `fast-uri` alone to 3.1.0 and re-running exits 0,
# because `fast-uri` appears in no `package.json` and 3.1.0 still
# satisfies the `^3.0.1` that `ajv` asks for. That class — the one
# #1401 actually was — needs a cross-lockfile version-map
# comparison, and this step is not it.
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Check bun.lock is in sync with package.json (no install)
working-directory: ${{ matrix.directory }}
run: bun install --frozen-lockfile --dry-run
# 24 for the same reason multi-runtime.yml pins its Node leg there:
# it is the `engines` floor the package claims to support, so the
# examples are exercised on the oldest runtime a user might have.
#
# Note the major alone is load-bearing here: `@angular/cli` 22
# declares `^22.22.3 || ^24.15.0 || >=26.0.0`, so the Angular legs
# need 24.15 or newer. `node-version: '24'` resolves to the latest
# 24.x and satisfies that; pinning a narrower 24.x would not.
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
cache: npm
cache-dependency-path: ${{ matrix.directory }}/package-lock.json
- name: Install (npm ci — fails on a desynced lockfile)
working-directory: ${{ matrix.directory }}
run: npm ci
- name: Build
working-directory: ${{ matrix.directory }}
run: npm run build
# #545 — until this job existed, nothing in CI ever executed an example.
# A framework change could break every snippet in the tree and the only
# signal would be a user running one.
#
# `tests/examples/examples.manifest.json` classifies all 78 standalone
# scripts as runnable-with-an-assertion or skipped-with-a-reason, and the
# runner fails when the manifest and the tree disagree in either
# direction — so a new example cannot silently opt out of the gate.
#
# Bun only. The cross-runtime question is `bun run smoke`'s, over cases
# written to be runtime-neutral; the examples are written for Bun (they
# reach for `Bun.sleep`, and they import `.ts` directly), so running them
# on Node and Deno would test the examples' portability rather than the
# framework's.
snippets:
runs-on: ubuntu-latest
# ~90 s of actual work; the ceiling is here so a snippet that learns to
# hang takes the job down with a red mark instead of burning the
# runner's default six hours.
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install
run: bun install --frozen-lockfile
- name: Run the examples
run: bun run test:examples