Skip to content

feat(ingest): single-transcript Claude hook fast-path + global --quiet #186

feat(ingest): single-transcript Claude hook fast-path + global --quiet

feat(ingest): single-transcript Claude hook fast-path + global --quiet #186

Workflow file for this run

name: napi build
# Build prebuilt napi-rs `.node` artifacts for every platform we publish to
# npm under `@relayburn/sdk-*`. PRs validate the matrix; the cutover publish
# workflow (`.github/workflows/publish.yml`) calls this workflow via
# `workflow_call` to produce the `.node` files it then stages into
# per-platform packages.
#
# Caches Cargo registry + git + target dir, plus pnpm store, so the matrix
# stays fast on the hot path.
on:
pull_request:
paths:
- 'crates/relayburn-sdk/**'
- 'crates/relayburn-sdk-node/**'
- 'packages/sdk-node/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.github/workflows/napi-build.yml'
push:
branches: [main]
paths:
- 'crates/relayburn-sdk/**'
- 'crates/relayburn-sdk-node/**'
- 'packages/sdk-node/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.github/workflows/napi-build.yml'
workflow_dispatch:
workflow_call:
# Publish runs the same matrix this workflow exercises on PRs. The
# caller downloads the uploaded `relayburn-sdk-<short>` artifacts and
# stages them into `packages/sdk-node/npm/<short>/` before
# `npm pack` + `npm publish`.
inputs:
release_version:
description: >-
Post-bump release version (e.g. "2.1.0") to bake into the
napi-rs binding via CARGO_PKG_VERSION. Optional — when empty
(PR/push runs) we skip the lockstep sed and build with the
on-disk workspace version unchanged.
required: false
type: string
permissions:
contents: read
concurrency:
group: napi-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: build ${{ matrix.target }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-apple-darwin
os: macos-15-intel
short: darwin-x64
- target: aarch64-apple-darwin
os: macos-14
short: darwin-arm64
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
short: linux-x64-gnu
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
short: linux-arm64-gnu
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v5
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '22.14.0'
cache: 'pnpm'
- name: Setup Rust toolchain
# rust-toolchain.toml at the repo root pins channel + components.
# `cross` handles the aarch64 linux target via container, so we
# only register the host target by default and add the matrix
# target explicitly when it's a supported native cross.
run: |
rustup toolchain install
rustup target add ${{ matrix.target }}
- name: Install aarch64-linux cross toolchain (linux-arm64 only)
if: matrix.target == 'aarch64-unknown-linux-gnu'
# `napi build` calls `cargo build --target aarch64-unknown-linux-gnu`
# directly (it does not auto-route through `cross`), so we need a real
# aarch64 cross-compiler available on the runner. `libsqlite3-sys` (a
# transitive dep via `rusqlite`) compiles bundled SQLite C source via
# `cc-rs`, which probes for `aarch64-linux-gnu-gcc`.
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Cache cargo registry + target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml', 'rust-toolchain.toml') }}
restore-keys: |
cargo-${{ runner.os }}-${{ matrix.target }}-
cargo-${{ runner.os }}-
- name: Install workspace deps
run: pnpm install --frozen-lockfile
- name: Lockstep Cargo.toml to release version
# When invoked via the publish workflow, sed the workspace
# `[workspace.package].version` (and the path-dep `version = "X.Y"`
# pin in `crates/relayburn-sdk-node/Cargo.toml`) so the napi-rs
# binding built below carries the post-bump `CARGO_PKG_VERSION`.
# Mirrors the lockstep regex in publish.yml and cli-build.yml.
#
# Skipped when `release_version` is empty (PR/push runs).
if: inputs.release_version != ''
run: |
set -euo pipefail
RUST_VER="${{ inputs.release_version }}"
RUST_MINOR=$(echo "$RUST_VER" | awk -F. '{print $1"."$2}')
echo "Rust workspace lockstep: $RUST_VER (minor pin: $RUST_MINOR)"
sed -i.bak -E "s/^version = \"[^\"]+\"$/version = \"$RUST_VER\"/" Cargo.toml
sed -i.bak -E "s|(relayburn-sdk = \\{ path = \"\\.\\./relayburn-sdk\", version = \")[^\"]+(\" \\})|\\1$RUST_MINOR\\2|" crates/relayburn-sdk-node/Cargo.toml
rm -f Cargo.toml.bak crates/relayburn-sdk-node/Cargo.toml.bak
- name: Build napi binding for ${{ matrix.target }}
# `napi build` reads `packages/sdk-node/package.json`'s `napi`
# block to learn the binary name, then dispatches to `cargo build`
# under the hood. The output is a per-target `.node` file plus a
# generated `binding.cjs` / `binding.d.ts`.
#
# We pass `src` as the positional `[destDir]` so all three outputs
# (`.node`, `binding.cjs`, `binding.d.ts`) land in `src/` next to
# `index.js`. The generated `binding.cjs` resolves the local `.node`
# via `existsSync(join(__dirname, 'index.<target>.node'))`, and
# `__dirname` is `packages/sdk-node/src/`, so co-locating the
# artifacts is the simplest way to make the local-binding branch
# actually fire (without it, the loader silently falls through to
# `require('@relayburn/sdk-<target>')`, which isn't installed in
# dev). The `--js` / `--dts` paths are relative to the destDir so
# `pnpm exec napi build … --js binding.cjs --dts binding.d.ts src`
# writes to `src/binding.{cjs,d.ts}` (passing `src/binding.cjs` plus
# `src` would double-prefix to `src/src/binding.cjs`).
#
# `--cargo-cwd` points at the binding crate (which lives under
# `crates/relayburn-sdk-node/`, not co-located with the npm package)
# so the `cargo metadata` step that resolves the cdylib succeeds.
# The CC_/linker env vars only take effect for the aarch64-linux leg
# (cargo ignores per-target vars when the host matches the target);
# native legs are unaffected.
working-directory: packages/sdk-node
env:
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
run: |
pnpm exec napi build \
--platform \
--release \
--target ${{ matrix.target }} \
--cargo-cwd ../../crates/relayburn-sdk-node \
--js binding.cjs \
--dts binding.d.ts \
src
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: relayburn-sdk-${{ matrix.short }}
path: packages/sdk-node/src/*.node
if-no-files-found: warn
retention-days: 7
- name: Bundle smoke test (esbuild)
# Confirms the umbrella facade resolves + bundles cleanly. Runs on
# every matrix leg so we catch platform-specific resolution issues
# (path casing, native-binding aliases) close to where they'd
# surface for an embedder.
working-directory: packages/sdk-node
run: node --test 'test/esbuild-smoke.test.js'
- name: Native SDK smoke test
# The suite runs the Node facade against the committed cli-golden
# ledger and checks stable result shapes for the exported verbs.
#
# Skipped on the aarch64-linux leg: that leg is a CROSS-COMPILE
# (host=ubuntu-latest x64, target=aarch64-unknown-linux-gnu), so
# the only `.node` artifact present is the arm64 binary. The
# x64 `node` interpreter on the runner cannot load it — and
# `binding.cjs` resolves the binding by `process.arch`, so it
# tries to load `index.linux-x64-gnu.node` (or the matching
# optionalDependency, which isn't installed in dev) and crashes
# the import before the test body's `t.skip` can fire. The other
# three legs (x64-darwin, arm64-darwin, x64-linux) are all native
# and exercise the same conformance contract; this leg's job is
# purely to validate the cross-compile build + artifact upload.
#
if: matrix.target != 'aarch64-unknown-linux-gnu'
working-directory: packages/sdk-node
env:
RELAYBURN_SDK_NAPI_BUILT: '1'
run: node --test 'test/conformance.test.js'