Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
test:
name: ${{ matrix.os }}
Expand Down Expand Up @@ -44,3 +47,145 @@ jobs:
- uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}

# Build every prebuild target the released package ships. Running this on
# PRs (not just on main) means a broken target is caught before merge; the
# Release workflow reuses these same jobs (via workflow_call) and publishes
# the artifacts they upload.
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# ── macOS: build both arches, lipo to universal ───────────────────
- name: darwin-universal
os: macos-latest
build: |
rustup target add x86_64-apple-darwin
yarn napi build --platform --release --esm --output-dir . --target aarch64-apple-darwin --js binding.js --dts binding.d.ts
yarn napi build --platform --release --esm --output-dir . --target x86_64-apple-darwin --js binding.js --dts binding.d.ts
yarn napi universalize --output-dir .
artifact: index.darwin-universal.node

# ── Windows ───────────────────────────────────────────────────────
- name: win32-x64
os: windows-latest
build: yarn napi build --platform --release --esm --output-dir . --target x86_64-pc-windows-msvc --js binding.js --dts binding.d.ts
artifact: index.win32-x64-msvc.node

- name: win32-arm64
os: windows-latest
build: |
rustup target add aarch64-pc-windows-msvc
yarn napi build --platform --release --esm --output-dir . --target aarch64-pc-windows-msvc --js binding.js --dts binding.d.ts
artifact: index.win32-arm64-msvc.node

# ── Linux glibc (native runners for each arch) ────────────────────
- name: linux-x64-gnu
os: ubuntu-latest
build: yarn napi build --platform --release --esm --output-dir . --target x86_64-unknown-linux-gnu --js binding.js --dts binding.d.ts
artifact: index.linux-x64-gnu.node

- name: linux-arm64-gnu
os: ubuntu-24.04-arm
build: yarn napi build --platform --release --esm --output-dir . --target aarch64-unknown-linux-gnu --js binding.js --dts binding.d.ts
artifact: index.linux-arm64-gnu.node

# ── Linux musl (Alpine via docker run on native-arch runners) ─────
# Job containers can't be used here: the runner refuses to exec JS
# actions (checkout, upload-artifact, ...) inside Alpine containers
# on arm64 hosts (actions/runner#801), so the "Build (docker)" step
# runs the whole alpine build via `docker run` instead. Rust comes
# from a version-pinned, checksum-verified rustup-init.
- name: linux-x64-musl
os: ubuntu-latest
docker: node:22-alpine@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920
rustup_url: https://static.rust-lang.org/rustup/archive/1.29.0/x86_64-unknown-linux-musl/rustup-init
rustup_sha256: 9cd3fda5fd293890e36ab271af6a786ee22084b5f6c2b83fd8323cec6f0992c1
build: |
rustup target add x86_64-unknown-linux-musl
yarn napi build --platform --release --esm --output-dir . --target x86_64-unknown-linux-musl --js binding.js --dts binding.d.ts
artifact: index.linux-x64-musl.node

- name: linux-arm64-musl
os: ubuntu-24.04-arm
docker: node:22-alpine@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920
rustup_url: https://static.rust-lang.org/rustup/archive/1.29.0/aarch64-unknown-linux-musl/rustup-init
rustup_sha256: 88761caacddb92cd79b0b1f939f3990ba1997d701a38b3e8dd6746a562f2a759
build: |
# napi assumes aarch64-musl is cross-compiled and would default the
# linker to aarch64-linux-musl-gcc; here gcc IS the native musl gcc.
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=gcc
rustup target add aarch64-unknown-linux-musl
yarn napi build --platform --release --esm --output-dir . --target aarch64-unknown-linux-musl --js binding.js --dts binding.d.ts
artifact: index.linux-arm64-musl.node

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: .nvmrc
cache: yarn
Comment thread
MarshallOfSound marked this conversation as resolved.
package-manager-cache: ${{ github.ref != 'refs/heads/main' }}

# Rust (stable) is preinstalled on the hosted runner images; the alpine
# docker builds install their own pinned rustup in "Build (docker)".
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
key: ${{ matrix.name }}

- run: yarn install --immutable

- name: Build
if: ${{ !matrix.docker }}
shell: bash
run: ${{ matrix.build }}

# Everything that the other entries do across separate steps — alpine
# deps, Rust, yarn install, build, strip, smoke test — has to happen
# inside the container here, since the host can't run musl binaries.
- name: Build (docker)
if: matrix.docker
run: |
docker run --rm -v "$PWD:/build" -w /build "${{ matrix.docker }}" sh -ec '
apk add --no-cache bash curl build-base cmake python3 git
curl --proto "=https" --tlsv1.2 -fsSL -o /tmp/rustup-init "${{ matrix.rustup_url }}"
echo "${{ matrix.rustup_sha256 }} /tmp/rustup-init" | sha256sum -c -
chmod +x /tmp/rustup-init
/tmp/rustup-init -y --profile minimal --default-toolchain stable
export PATH="$HOME/.cargo/bin:$PATH"
yarn install --immutable
${{ matrix.build }}
node scripts/strip-binding-fallbacks.js
yarn test
'
sudo chown -R "$(id -u):$(id -g)" .

- name: Strip binding.js package fallbacks
run: node scripts/strip-binding-fallbacks.js

- name: Smoke test (native targets only)
if: ${{ !matrix.docker && (!contains(matrix.name, 'arm64') || contains(matrix.os, 'arm') || matrix.os == 'macos-latest') }}
run: yarn test

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.name }}
path: ${{ matrix.artifact }}
if-no-files-found: error

# binding.js / binding.d.ts are gitignored; ship one canonical copy from
# this job for the release step to pick up.
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.name == 'linux-x64-gnu'
with:
name: binding
path: |
binding.js
binding.d.ts
if-no-files-found: error
133 changes: 8 additions & 125 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,144 +1,27 @@
name: Release

# On every push to main: run CI, build all 7 native targets, then let
# semantic-release decide the version from the conventional commits, publish the
# fat package to npm, and cut a GitHub release. No-op if no releasable commits.
# On every push to main: run the full CI suite (tests, audit, and all 7 native
# target builds — the same jobs PRs run), then let semantic-release decide the
# version from the conventional commits, publish the fat package to npm, and
# cut a GitHub release. No-op if no releasable commits.

on:
push:
branches: [main]

permissions: {}

env:
CARGO_TERM_COLOR: always

jobs:
# Reuse the full PR test suite as a release gate.
test:
# Reuse the full PR suite as the release gate; the build jobs in it upload
# the prebuild artifacts that the release job below packages.
ci:
permissions:
contents: read
uses: ./.github/workflows/ci.yml

build:
name: ${{ matrix.name }}
needs: test
permissions:
contents: read
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
# ── macOS: build both arches, lipo to universal ───────────────────
- name: darwin-universal
os: macos-latest
build: |
rustup target add x86_64-apple-darwin
yarn napi build --platform --release --esm --output-dir . --target aarch64-apple-darwin --js binding.js --dts binding.d.ts
yarn napi build --platform --release --esm --output-dir . --target x86_64-apple-darwin --js binding.js --dts binding.d.ts
yarn napi universalize --output-dir .
artifact: index.darwin-universal.node

# ── Windows ───────────────────────────────────────────────────────
- name: win32-x64
os: windows-latest
build: yarn napi build --platform --release --esm --output-dir . --target x86_64-pc-windows-msvc --js binding.js --dts binding.d.ts
artifact: index.win32-x64-msvc.node

- name: win32-arm64
os: windows-latest
build: |
rustup target add aarch64-pc-windows-msvc
yarn napi build --platform --release --esm --output-dir . --target aarch64-pc-windows-msvc --js binding.js --dts binding.d.ts
artifact: index.win32-arm64-msvc.node

# ── Linux glibc (native runners for each arch) ────────────────────
- name: linux-x64-gnu
os: ubuntu-latest
build: yarn napi build --platform --release --esm --output-dir . --target x86_64-unknown-linux-gnu --js binding.js --dts binding.d.ts
artifact: index.linux-x64-gnu.node

- name: linux-arm64-gnu
os: ubuntu-24.04-arm
build: yarn napi build --platform --release --esm --output-dir . --target aarch64-unknown-linux-gnu --js binding.js --dts binding.d.ts
artifact: index.linux-arm64-gnu.node

# ── Linux musl (Alpine container on native-arch runner) ───────────
- name: linux-x64-musl
os: ubuntu-latest
container: node:22-alpine@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920
setup: apk add --no-cache bash curl build-base cmake python3 git
build: |
rustup target add x86_64-unknown-linux-musl
yarn napi build --platform --release --esm --output-dir . --target x86_64-unknown-linux-musl --js binding.js --dts binding.d.ts
artifact: index.linux-x64-musl.node

- name: linux-arm64-musl
os: ubuntu-24.04-arm
container: node:22-alpine@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920
setup: apk add --no-cache bash curl build-base cmake python3 git
build: |
rustup target add aarch64-unknown-linux-musl
yarn napi build --platform --release --esm --output-dir . --target aarch64-unknown-linux-musl --js binding.js --dts binding.d.ts
artifact: index.linux-arm64-musl.node

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Container setup
if: matrix.setup
run: ${{ matrix.setup }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
if: ${{ !matrix.container }}
with:
node-version-file: .nvmrc
cache: yarn

# The alpine containers ship no Rust; hosted runners have stable preinstalled.
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable # zizmor: ignore[superfluous-actions]
if: matrix.container
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
key: ${{ matrix.name }}

- run: yarn install --immutable

- name: Build
shell: bash
run: ${{ matrix.build }}

- name: Strip binding.js package fallbacks
run: node scripts/strip-binding-fallbacks.js

- name: Smoke test (native targets only)
if: ${{ !contains(matrix.name, 'arm64') || contains(matrix.os, 'arm') || matrix.os == 'macos-latest' }}
run: yarn test

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.name }}
path: ${{ matrix.artifact }}
if-no-files-found: error

# binding.js / binding.d.ts are gitignored; ship one canonical copy from
# this job for the release step to pick up.
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.name == 'linux-x64-gnu'
with:
name: binding
path: |
binding.js
binding.d.ts
if-no-files-found: error

release:
name: Release
needs: build
needs: ci
runs-on: ubuntu-latest
environment: npm-trusted-publisher
permissions:
Expand Down
Loading