Skip to content

Commit 176bb69

Browse files
committed
ci: check the prebuilt addon binds on every supported Node major
The existing jobs compile the addon with the same Node they then run it under, so an incompatible prebuild cannot show up there. Build one prebuild set and load it from each major a consumer may run, calling setupCore so that lazily bound V8 symbols are resolved rather than only opening the file.
1 parent f387eec commit 176bb69

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,41 @@ jobs:
2727
- run: pnpm install --frozen-lockfile --prefer-offline
2828
- run: pnpm turbo run lint typecheck test
2929

30+
native-abi:
31+
runs-on: "ubuntu-latest"
32+
name: Native addon ABI compatibility
33+
env:
34+
# The flags the runner passes in a real benchmark process.
35+
NODE_OPTS: "--interpreted-frames-native-stack --allow-natives-syntax"
36+
steps:
37+
- uses: "actions/checkout@v4"
38+
with:
39+
fetch-depth: 0
40+
submodules: true
41+
- uses: pnpm/action-setup@v4
42+
- uses: actions/setup-node@v6
43+
with:
44+
cache: pnpm
45+
node-version-file: .nvmrc
46+
- name: Restore turbo cache
47+
uses: ./.github/actions/turbo-cache
48+
with:
49+
key-suffix: native-abi
50+
- run: pnpm install --frozen-lockfile --prefer-offline
51+
# One prebuild set, built once, then loaded by every supported Node
52+
# major. The other jobs compile the addon with the same Node they run it
53+
# under, so they cannot see an ABI mismatch.
54+
- run: pnpm turbo run build --filter=@codspeed/core
55+
56+
- uses: actions/setup-node@v6
57+
with:
58+
node-version: "22"
59+
- run: node ${{ env.NODE_OPTS }} scripts/assert-native-binding.cjs
60+
- uses: actions/setup-node@v6
61+
with:
62+
node-version: "24"
63+
- run: node ${{ env.NODE_OPTS }} scripts/assert-native-binding.cjs
64+
3065
list-examples:
3166
runs-on: "ubuntu-latest"
3267
name: List examples

‎scripts/assert-native-binding.cjs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env node
2+
// Loads the prebuilt native addon through the same resolution a consumer gets
3+
// and runs the perf-map handler. Symbols in the addon are bound lazily, so a
4+
// prebuild that is incompatible with the running ABI loads without complaint
5+
// and only dies once a V8 entry point is actually called.
6+
const fs = require("fs");
7+
const path = require("path");
8+
9+
const core = require(
10+
path.join(__dirname, "..", "packages", "core", "dist", "index.cjs.js"),
11+
);
12+
13+
const runtime = `Node ${process.version} (ABI ${process.versions.modules}, ${process.platform}-${process.arch})`;
14+
15+
core.setupCore();
16+
core.teardownCore();
17+
18+
const perfMap = path.join("/tmp", `perf-${process.pid}.map`);
19+
const entries = fs
20+
.readFileSync(perfMap, "utf8")
21+
.split("\n")
22+
.filter((line) => line.length > 0);
23+
fs.unlinkSync(perfMap);
24+
25+
if (entries.length === 0) {
26+
throw new Error(`${runtime}: the perf map handler produced no entries`);
27+
}
28+
29+
console.log(`${runtime}: bound, ${entries.length} perf map entries`);

0 commit comments

Comments
 (0)