From efde9471073163a0ace09bb3b10ac97d85a442bd Mon Sep 17 00:00:00 2001 From: ZhenghuaBao Date: Tue, 18 Aug 2026 17:51:58 +0800 Subject: [PATCH 1/2] ci: run the Node test suite on ubuntu-latest (Node 20 + 24) The 11 test files under scripts/*.test.mjs had no CI job, so they only ever ran on contributor machines. That left a real blind spot: on Windows several of them abort inside libuv (exit 0xC0000409, "Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), src/win/async.c") instead of failing an assertion. Because many of these tests assert on the exit status of a spawned child -- report.test.mjs checks `r.status === 0` -- that native crash is indistinguishable from a genuine regression when read locally. Linux is both the action's real runtime and free of that libuv bug, so this job is the authoritative signal. Matrix rationale: 20 - what action.yml pins via actions/setup-node; the production runtime. 24 - current Node, to catch forward-compat breakage before a runner bump. The glob is left unquoted so the shell expands it: `node --test` only gained native glob support in Node 21, so a quoted pattern would be treated as a literal filename on the Node 20 leg. No secrets needed -- the tests bind loopback HTTP servers and use dummy credentials, so this is safe on fork PRs and runs with contents:read only. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/test.yml | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..172b723 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,57 @@ +# Run the Node test suite on Linux. +# +# WHY THIS EXISTS: the suite under scripts/*.test.mjs had no CI job at all, so it +# only ever ran on contributor laptops. On Windows several of these tests die with +# a native libuv abort (exit 0xC0000409, "Assertion failed: !(handle->flags & +# UV_HANDLE_CLOSING), src/win/async.c") rather than a real assertion failure. Since +# many tests assert on the exit code of a spawned child (e.g. report.test.mjs +# asserts `r.status === 0`), that crash is indistinguishable from a genuine bug +# when read from a Windows terminal. Linux is both the real runtime for this action +# and free of that libuv bug, so this job is the authoritative verdict. +# +# The matrix is deliberate: +# 20 - the version the action itself pins (actions/setup-node in action.yml). +# This is what the tested code actually runs on in production. +# 24 - current Node; catches forward-compat breakage before a runner bump and +# matches what contributors tend to have installed locally. +# +# No secrets are required: the tests spin up loopback HTTP servers and use dummy +# credentials, so this is safe to run on fork PRs. + +name: Test + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + # Report every version's result, not just the first failure. + fail-fast: false + matrix: + node-version: ["20", "24"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: node --version + run: node --version + + # Unquoted on purpose: the shell expands the glob into an explicit file + # list. `node --test` only gained native glob support in Node 21, so a + # quoted pattern would be read as a literal filename on the Node 20 leg. + - name: Run tests + run: node --test scripts/*.test.mjs From 19ab6db9c53f6c6d1d2f23649735de5cb72af811 Mon Sep 17 00:00:00 2001 From: ZhenghuaBao Date: Tue, 18 Aug 2026 18:47:53 +0800 Subject: [PATCH 2/2] ci: simplify workflow comments Trim the rationale block to the two notes that affect maintenance: why the matrix is 20 + 24, and why the glob is unquoted. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/test.yml | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 172b723..fcc164f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,22 +1,11 @@ # Run the Node test suite on Linux. # -# WHY THIS EXISTS: the suite under scripts/*.test.mjs had no CI job at all, so it -# only ever ran on contributor laptops. On Windows several of these tests die with -# a native libuv abort (exit 0xC0000409, "Assertion failed: !(handle->flags & -# UV_HANDLE_CLOSING), src/win/async.c") rather than a real assertion failure. Since -# many tests assert on the exit code of a spawned child (e.g. report.test.mjs -# asserts `r.status === 0`), that crash is indistinguishable from a genuine bug -# when read from a Windows terminal. Linux is both the real runtime for this action -# and free of that libuv bug, so this job is the authoritative verdict. +# Node 20 is what action.yml pins via actions/setup-node, so it is the version +# the tested code actually runs on. Node 24 is included to catch forward-compat +# breakage before a runner bump. # -# The matrix is deliberate: -# 20 - the version the action itself pins (actions/setup-node in action.yml). -# This is what the tested code actually runs on in production. -# 24 - current Node; catches forward-compat breakage before a runner bump and -# matches what contributors tend to have installed locally. -# -# No secrets are required: the tests spin up loopback HTTP servers and use dummy -# credentials, so this is safe to run on fork PRs. +# The tests bind loopback HTTP servers and use dummy credentials, so no secrets +# are required. name: Test @@ -36,7 +25,6 @@ jobs: test: runs-on: ubuntu-latest strategy: - # Report every version's result, not just the first failure. fail-fast: false matrix: node-version: ["20", "24"] @@ -47,11 +35,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: node --version - run: node --version - # Unquoted on purpose: the shell expands the glob into an explicit file # list. `node --test` only gained native glob support in Node 21, so a # quoted pattern would be read as a literal filename on the Node 20 leg. - - name: Run tests - run: node --test scripts/*.test.mjs + - run: node --test scripts/*.test.mjs