fix(release): sync package-lock with the platform optionalDependencies #61
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 ci --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." |