Skip to content

Commit cfbab0d

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 3949b24 commit cfbab0d

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,45 @@ 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 Node major a
52+
# consumer may run. The other jobs compile the addon with the same Node
53+
# they run it 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+
- uses: actions/setup-node@v6
65+
with:
66+
node-version: "26"
67+
- run: node ${{ env.NODE_OPTS }} scripts/assert-native-binding.cjs
68+
3069
list-examples:
3170
runs-on: "ubuntu-latest"
3271
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)