-
Notifications
You must be signed in to change notification settings - Fork 0
385 lines (358 loc) · 16.1 KB
/
Copy pathci.yml
File metadata and controls
385 lines (358 loc) · 16.1 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
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
name: CI
on:
push:
branches: [main, develop]
pull_request:
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# ---------------------------------------------------------------- quality
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache-dependency-path: src/bindings/nodejs/package-lock.json
- run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
- run: npm run lint
working-directory: src/bindings/nodejs
# ------------------------------------------------------------------ tests
# The matrix is the whole point of this workflow: "works on my machine" is
# exactly the failure mode this project is trying to eliminate, so every
# supported OS/arch/Node combination has to run the same suites.
test:
name: ${{ matrix.name }} / node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { name: linux-x64, os: ubuntu-latest, node: 18 }
- { name: linux-x64, os: ubuntu-latest, node: 20 }
- { name: linux-x64, os: ubuntu-latest, node: 22 }
- { name: linux-arm64, os: ubuntu-24.04-arm, node: 22 }
- { name: macos-arm64, os: macos-latest, node: 18 }
- { name: macos-arm64, os: macos-latest, node: 22 }
# macos-15-intel, not macos-13: GitHub retired the macOS 13 labels on
# 2025-12-04, and a job asking for a retired label is not rejected --
# it queues for a machine that will never arrive, so this leg sat
# pending through every run and took the all-green gate with it.
# This is the last x86_64 macOS image Actions will offer.
- { name: macos-x64, os: macos-15-intel, node: 22 }
- { name: windows-x64, os: windows-latest, node: 20 }
- { name: windows-x64, os: windows-latest, node: 22 }
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.name }}
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
cache: npm
cache-dependency-path: src/bindings/nodejs/package-lock.json
- name: Install clang (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y clang
# inotify's default watch limit is low enough that a recursive watch over
# a large tree can exhaust it. Raise it so a genuine bug is not masked by
# an environment limit -- and so the limit-exhaustion test stays
# meaningful rather than firing spuriously.
- name: Raise inotify limits (Linux)
if: runner.os == 'Linux'
run: |
sudo sysctl -w fs.inotify.max_user_watches=524288
sudo sysctl -w fs.inotify.max_user_instances=512
- name: Build C hash engine
run: make build-core
- name: C engine tests
run: make test-core
# retrigger-nodejs-bindings used to be excluded here. It is a `cdylib`
# whose napi_* symbols come from the Node process that loads it, and
# under napi 2 a standalone `cargo test` harness had no host for them:
# it failed to link on Windows and, on x86-64 Linux, linked but died at
# startup resolving napi_reference_unref through a GOT relocation. napi 3
# resolves those symbols at load time instead (napi-sys `dyn-symbols`, on
# by default), so the harness now carries no napi_* imports at all and
# one command is honest on every runner.
- name: Rust tests
run: cargo test --workspace --release
- name: Install JS dependencies
run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
# Build the addon this package actually ships, so the native-parity suite
# (parity-native.test.mjs) runs instead of skipping. Without this step a
# green JS run never proves the fallback matches the real Rust engine --
# the one guarantee the whole package rests on. Gated to match the JS test
# legs below: the addon is a Rust build (Node-version independent), but its
# only consumer here is the vitest run, which the Node 18 legs skip.
- name: Build native addon
if: matrix.node != 18
run: npm run build
working-directory: src/bindings/nodejs
# Unguarded on every leg, Node 18 included, and vite is held at 6 to keep
# it that way. Vitest 4 declares node ^20 || ^22 || >=24, but that is
# advisory and it runs on 18 regardless; vite 7+ is the real constraint,
# because it bundles rolldown, which imports node:util#styleText (Node
# 20.12+). With vite 7+ installed the failure is a startup error -- vitest
# loads this config through vite -- so it costs the entire suite on the
# oldest Node engines.node claims, not just the dev-server file.
- name: JavaScript tests
run: npm test
working-directory: src/bindings/nodejs
# The daemon npm package is a shim over a per-platform Rust binary. This
# proves the shim, the shipped config, and the documented no-binary
# degradation from Node. Plain node:test, so it runs on every leg
# including Node 18.
- name: Daemon package smoke test
run: node scripts/test-daemon.js
working-directory: src/daemon
# The direct answer to "will it work when I install it somewhere else":
# pack the tarball, install it into a clean directory, and require it.
- name: Packaged install proof
run: npm run test:pack
working-directory: src/bindings/nodejs
# FreeBSD has no GitHub-hosted runner. Same free `vmactions/freebsd-vm`
# guest used by the release build: prove the Node package builds, passes its
# suite, and packs on a real FreeBSD x64 kernel. The full Rust workspace
# suite stays on the GitHub-hosted OS matrix above — this leg is the Node
# addon contract for the platform the matrix previously omitted.
test-freebsd:
name: freebsd-x64 / node 22
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@v7
- name: Build and test on FreeBSD
uses: vmactions/freebsd-vm@v1
with:
release: '14.3'
usesh: true
copyback: false
prepare: |
pkg install -y gmake curl
pkg install -y node22 || pkg install -y node
# FreeBSD's node package ships node but not npm; it is a separate port.
pkg install -y npm-node22 || pkg install -y npm
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
run: |
set -eu
. "$HOME/.cargo/env"
cd src/bindings/nodejs
# `npm install`, not `npm ci`: FreeBSD's pkg ships a newer, stricter npm
# that fails `npm ci` because the unpublished @retrigger/core-* platform
# optionalDependencies have no resolved nodes in the lock (they 404 until
# the release publishes them). install reconciles instead of strictly
# validating, so the leg is not blocked by a chicken-and-egg.
npm install --no-audit --no-fund
npm run build
npm test
npm run test:pack
# ------------------------------------------------------------- bundler-legs
# test/vite.test.mjs and test/webpack.test.mjs run against the pinned vite@6
# and webpack@5 on every leg above via plain `npm test`. This job re-runs
# those exact same suites retargeted at vite@7 and @rspack/core (see
# vitest.vite7.config.mjs / vitest.rspack.config.mjs) plus the Rspack-only
# persistent-cache case, on the one Node version (22) that clears every
# floor involved: vite@7's rolldown needs node:util#styleText (20.12+),
# @rspack/core needs 20.19+/22.12+, and Astro (already exercised by the
# matrix's Node-22 legs through plain `npm test`, self-skipped elsewhere --
# see test/astro.test.mjs) needs 22.12+. Both scripts are also
# version-gated internally, so this job stays a true no-op rather than a
# failure if that floor ever moves out from under it.
bundler-legs:
name: Bundler compatibility (vite@7, Rspack, Astro)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: bundler-legs
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache-dependency-path: src/bindings/nodejs/package-lock.json
- run: sudo apt-get update && sudo apt-get install -y clang
- run: make build-core
- run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
- run: npm run build
working-directory: src/bindings/nodejs
- name: vite@7
run: npm run test:vite7
working-directory: src/bindings/nodejs
- name: Rspack
run: npm run test:rspack
working-directory: src/bindings/nodejs
- name: Astro dev server
run: npx vitest run test/astro.test.mjs
working-directory: src/bindings/nodejs
# ------------------------------------------------------------------- musl
# Alpine is a separate risk surface from Debian: different libc, and the
# package's libc detection has to pick the musl binaries. It has been wrong
# before, so it gets its own job rather than being assumed from linux-x64.
test-musl:
name: linux-x64-musl
runs-on: ubuntu-latest
container:
image: node:22-alpine
steps:
# Before the checkout, deliberately. Without git in the image the action
# falls back to the REST tarball, which is `git archive` and therefore
# honours the export-ignore rules in .gitattributes -- so this job alone
# would run against a tree missing tools/benchmarks and fail on the
# scripts-point-at-real-files check for a reason that is not a defect.
- name: Install git so the checkout is a clone, not an export
run: apk add --no-cache git
- uses: actions/checkout@v7
- name: Install toolchain
run: apk add --no-cache build-base clang clang-dev llvm-dev rust cargo make bash python3
- run: make build-core
- run: make test-core
- run: cargo test --workspace --release
- run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
- run: npm test
working-directory: src/bindings/nodejs
# ----------------------------------------------------------- sanitizers
sanitizers:
name: Sanitizers (ASan + UBSan)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: sudo apt-get update && sudo apt-get install -y clang
- name: C engine under ASan/UBSan
run: make test-core-asan
env:
CC: clang
# Compile-only, so it needs no libFuzzer runtime and gives the same answer
# on every machine: it stops a fuzz target from rotting unnoticed between
# the manual campaigns that actually run them.
- name: Fuzz targets still build
run: make check-fuzz
env:
CC: clang
# --------------------------------------------------------- no-native path
# Proves the JavaScript fallback: the package must load and work with no
# native addon present at all. This is the guarantee that an unsupported
# platform degrades instead of throwing at require() time.
fallback:
name: JS fallback (no native binary)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache-dependency-path: src/bindings/nodejs/package-lock.json
- run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
# Scoped to this package's own artifacts. The unqualified sweep this
# replaces also deleted node_modules/@rollup/*/rollup.*.node, and vitest
# loads its config through vite, so the suite died at startup with
# MODULE_NOT_FOUND instead of proving anything about the fallback.
- name: Remove this package's native artifacts
run: find . -name '*.node' -not -path './node_modules/*' -print -delete
working-directory: src/bindings/nodejs
# Deliberately without RETRIGGER_FORCE_JS. The absent binary above is the
# real condition being proved; the environment override is a different
# guarantee, is covered in loader.test.mjs, and setting it here also
# disabled the mock addon that the parity suite installs to exercise the
# native path -- so those tests failed asking for an engine this job had
# forbidden. `parity-native.test.mjs` skips itself when no addon exists.
- name: Suite must pass with no addon
run: npm test
working-directory: src/bindings/nodejs
# The JavaScript engine covers a tree with one recursive watch on Windows,
# because a per-directory handle there drops most of a burst. That strategy
# would otherwise be exercised only by the Windows jobs, which are the
# slowest to report and the least pleasant to debug. macOS serves both, so it
# can hold the same code to the whole suite on every push.
fallback-recursive:
name: JS fallback (recursive watch strategy)
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache-dependency-path: src/bindings/nodejs/package-lock.json
- run: npm ci --no-audit --no-fund
working-directory: src/bindings/nodejs
- name: Remove this package's native artifacts
run: find . -name '*.node' -not -path './node_modules/*' -print -delete
working-directory: src/bindings/nodejs
- name: Suite must pass with one watch per tree
run: npm test
working-directory: src/bindings/nodejs
env:
RETRIGGER_JS_RECURSIVE: '1'
# ------------------------------------------------------------------- MSRV
msrv:
name: Minimum supported Rust version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Read rust-version from the workspace manifest
id: msrv
run: echo "version=$(grep -m1 '^rust-version' Cargo.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.version }}
- run: sudo apt-get update && sudo apt-get install -y clang
- run: cargo check --workspace --all-targets
# ------------------------------------------------------------------ audit
audit:
name: Dependency audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v7
with:
node-version: 22
- name: cargo audit
run: |
cargo install cargo-audit --locked
cargo audit
- name: npm audit
run: npm audit --audit-level=moderate
working-directory: src/bindings/nodejs
# ------------------------------------------------------------------- gate
# A single required check, so branch protection does not need updating every
# time the matrix changes.
ci-passed:
name: CI passed
if: always()
needs:
[lint, test, test-freebsd, bundler-legs, test-musl, sanitizers, fallback, fallback-recursive, msrv]
runs-on: ubuntu-latest
steps:
- name: Fail if any dependency failed
run: |
if echo '${{ join(needs.*.result, ' ') }}' | grep -qE 'failure|cancelled'; then
echo "One or more required jobs did not succeed."
exit 1
fi
echo "All required jobs succeeded."