Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
145 commits
Select commit Hold shift + click to select a range
3bccd8b
feat(ops): unified release-cli flow (Makefile + ops/release.sh)
secxena Apr 28, 2026
ba6174b
chore: extend release admin defaults
secxena Apr 28, 2026
98a8d86
feat: extend detection telemetry signals
secxena Apr 28, 2026
09515e9
feat(ops): release-classify verb (Phase 2)
secxena Apr 28, 2026
2785b77
feat(ops): release-catalog verbs (Phase 3)
secxena Apr 28, 2026
30d7c03
changes fixed for windows
prabhatkashyap902 Apr 29, 2026
3bab2c8
feat(ops): cross-env status + extended diff (Phase 4)
secxena Apr 29, 2026
eb49b86
Merge pull request #57 from soth-ai/feat/ops-makefile-phase4-status-diff
secxena Apr 29, 2026
9e77855
style: cargo fmt
prabhatkashyap902 Apr 29, 2026
33251d5
Merge pull request #56 from soth-ai/f/binaryFixForWindows
secxena Apr 29, 2026
57fbf48
refactor(sdk): PR 1 - feature-gate native deps for SDK/WASM consumers
secxena Apr 29, 2026
e66a6bd
refactor(sdk): PR 2 - split ProxyContext into Identity/Transport/Attr…
secxena Apr 29, 2026
2abf879
refactor(sdk): PR 3 - pre-parsed entry point in soth-detect
secxena Apr 29, 2026
3e09910
refactor(sdk): PR 4 - Send + Sync audit + concurrent stress tests
secxena Apr 29, 2026
a739494
refactor(sdk): PR 5 - conformance harness for cross-lane parity
secxena Apr 29, 2026
d68fb20
docs(sdk): pre-flight specs - Decision API + WASM trust boundary
secxena Apr 30, 2026
b5c6f6d
feat(sdk): Phase 0 - soth-sdk-core facade scaffolding
secxena Apr 30, 2026
ba5d83b
feat(sdk): conformance facade lane — catches SothSdk drift vs direct …
secxena Apr 30, 2026
088b950
feat(sdk): Phase 1 - soth-py PyO3 binding scaffold
secxena Apr 30, 2026
026fa11
feat(sdk): Phase 1 - soth-node napi-rs binding scaffold
secxena Apr 30, 2026
c084831
feat(sdk): Phase 1 - streaming wrapper for Python + Node bindings
secxena Apr 30, 2026
76f336b
feat(sdk): Phase 1 - background HTTPS telemetry shipper
secxena Apr 30, 2026
da72b10
feat(sdk): Phase 1 - per-call CallContext + contextvars/AsyncLocalSto…
secxena Apr 30, 2026
32e52ff
feat(sdk): Phase 1 - auto-instrumentation for OpenAI + Anthropic
secxena Apr 30, 2026
0589288
feat(sdk): Phase 1.5 - Cohere/Google/Mistral Python + Anthropic Node …
secxena Apr 30, 2026
6a1cb29
feat(sdk): Phase 1.5 - LangChain / LlamaIndex / LiteLLM / Vercel AI i…
secxena Apr 30, 2026
1f6d79d
ci(sdk): Phase 2 - CI matrix for Rust + Python wheels + Node binaries
secxena Apr 30, 2026
d498d1e
feat(sdk): Phase 2 - FFI conformance lane (Python + Node)
secxena Apr 30, 2026
361bbd0
feat(sdk): Phase 4 - Go SDK + edge runtime scaffolds (WASM target ver…
secxena Apr 30, 2026
f8977f0
feat(sdk): make HMAC key optional in v1
secxena Apr 30, 2026
b0f4041
ci(sdk): fix fmt + missing ParseSource::Sdk match arms
secxena Apr 30, 2026
2dd420f
Merge pull request #58 from soth-ai/sdk-refactor
secxena Apr 30, 2026
fb7b749
Merge pull request #59 from soth-ai/detect-extend
secxena Apr 30, 2026
7e6c927
fix(proxy): graceful child rotation on primary-network change
secxena Apr 30, 2026
b7547f0
Merge pull request #60 from soth-ai/fix/proxy-network-change-reload
secxena Apr 30, 2026
18434f0
feat(classify): UseCaseLabelReason discriminator + WARN logs at silen…
secxena Apr 30, 2026
7d24b03
fix(classify): wire collision_response_stability through pipeline + e…
secxena Apr 30, 2026
cec8652
feat(sync): ship Tier A classification fields end-to-end
secxena Apr 30, 2026
736e8b4
feat(proxy): track classify worker panics with classify_panic_dropped…
secxena Apr 30, 2026
4c396a0
fix(classify): cosine_distance handles non-normalized embedding_centroid
secxena Apr 30, 2026
66276fd
Merge pull request #61 from soth-ai/fix/cosine-distance-l2-norm
secxena Apr 30, 2026
512fe3a
fix(sdk-ci): unbreak ffi-conformance for Node + Python
secxena Apr 30, 2026
aba4d27
fix(sdk-ci): commit package-lock.json so setup-node cache resolves
secxena Apr 30, 2026
5ce72d2
fix(sdk-ci): unbreak node-binaries cross-compile + musl jobs
secxena Apr 30, 2026
dcebe98
fix(sdk-ci): refresh Rust inside musl docker images
secxena Apr 30, 2026
64f2398
fix(cli): raise default capture_max_body_bytes to 64 MiB
secxena May 1, 2026
51e1f80
Merge remote-tracking branch 'origin/staging' into sdk-build
secxena May 1, 2026
e198772
Merge pull request #62 from soth-ai/classify-fix
secxena May 1, 2026
b5f5fa3
fix(sdk-ci): cross-build x86_64-apple-darwin from arm64 macos-14
secxena May 1, 2026
99de5fb
fix(sdk-ci): manylinux before-script ran apt-get unconditionally
secxena May 1, 2026
092ad28
fix(sdk-ci): unbreak python-wheels aarch64 + macos x86_64
secxena May 1, 2026
e5941f3
fix(sdk-ci): aarch64 wheel — use native manylinux image under QEMU
secxena May 1, 2026
b582147
style(sdk): cargo fmt --all
secxena May 2, 2026
88226bb
Merge pull request #63 from soth-ai/sdk-build
secxena May 2, 2026
1af3dc5
fix(cli): one-curl CA trust install on macOS + Windows
secxena May 2, 2026
ff558b7
Merge pull request #64 from soth-ai/fix/macos-windows-ca-trust-one-curl
secxena May 3, 2026
54e1152
fix(sync): drop total timeout for bundle fetches
secxena May 3, 2026
f572377
Merge pull request #65 from soth-ai/fix/bundle-fetch-slow-network
secxena May 3, 2026
d848762
fix(cli): raise proxy startup timeout for cold-install Windows
secxena May 3, 2026
938e1b6
Merge pull request #66 from soth-ai/fix/proxy-startup-timeout-windows…
secxena May 3, 2026
bbd86b9
style(cli): cargo fmt --all
secxena May 3, 2026
d2133e2
fix(proxy): make is_shadow_it destination-based for SSPM discovery
secxena May 3, 2026
f9d2c8e
chore: drop accidentally-committed playwright screenshots
secxena May 3, 2026
2d2a760
refactor(proxy): make is_shadow_it a parser-coverage signal
secxena May 3, 2026
1772e07
Merge pull request #67 from soth-ai/fix/shadow-it-destination-based-d…
secxena May 3, 2026
e94eed0
fix(cli): land Windows network-change watcher (was a no-op stub)
secxena May 4, 2026
1f98a35
Merge pull request #68 from soth-ai/fix/network-watcher-windows-imple…
secxena May 4, 2026
8d57093
refactor(api-types): extract shared cloud wire contract from soth-sync
secxena May 4, 2026
3352c80
Merge pull request #69 from soth-ai/refactor/extract-soth-api-types-s…
secxena May 4, 2026
16e3d5e
added details of device
prabhatkashyap902 May 5, 2026
0934dd5
device name and test passeed
prabhatkashyap902 May 5, 2026
25bdf78
cursor tracker now 15sec poll and track from wal of vscdb of cursor
prabhatkashyap902 May 6, 2026
fb5a0b0
feat(classify): promote 5 use-case labels surfaced by 400k retrain
secxena May 6, 2026
41257fa
Merge pull request #72 from soth-ai/feat/usecase-labels-from-400k-corpus
secxena May 6, 2026
4befa31
fix(extensions): drop dead `current` subdir from historian bundle_path
secxena May 6, 2026
a949d03
ci: scope expensive workflows to bindings-only on PRs + add concurren…
secxena May 6, 2026
f41f3e0
Merge pull request #71 from soth-ai/f/deviceIdFix
secxena May 6, 2026
bb7e177
fix(historian): unbreak sqlite incremental_reads test after PR #71
secxena May 6, 2026
3abb95a
fix(historian): wire ClassifyEnricher into watch + standalone paths
secxena May 6, 2026
1de38bd
chore: apply rustfmt to soth-classify model.rs assertion
secxena May 6, 2026
604b625
Merge pull request #73 from soth-ai/fix/historian-watch-enrich-and-cu…
secxena May 6, 2026
c07e476
ops(release): pin prod R2 publish to wrangler 4.75.0 via npx
secxena May 6, 2026
0da7758
perf(historian): dedup before classify, raise watch poll to 60s
secxena May 6, 2026
8c2c0e9
Merge pull request #75 from soth-ai/perf/historian-dedup-before-enrich
secxena May 6, 2026
2abb989
perf(historian): hard-floor watch cycle at 60s + drop SQLite sidecar …
secxena May 6, 2026
2b7b7f8
fix(cli): bump generated-yaml timeouts to streaming-friendly defaults
secxena May 6, 2026
496c1d6
feat(cli): yaml knob to disable historian extension
secxena May 6, 2026
eaf5a77
feat(cli): historian as subprocess (Option A) for runtime isolation
secxena May 6, 2026
48dab96
fix(cli): captive-portal + network-change + soth-off resilience
secxena May 7, 2026
2018860
chore: cargo fmt
secxena May 7, 2026
3e4bb55
Merge pull request #76 from soth-ai/fix/captive-portal-network-resili…
secxena May 7, 2026
2b4bfed
Merge pull request #74 from soth-ai/ops/pin-wrangler-4.75
secxena May 7, 2026
f5b1c75
feat(code): foundation schema and classify hook entry (Group 1)
secxena May 7, 2026
09e314e
feat(code): extension scaffolding (Group 2)
secxena May 7, 2026
c062d9a
feat(code): hook handler smoke E2E (Group 3 — Phase 0 closes)
secxena May 7, 2026
11445ac
feat(code): Claude Code adapter + fixture corpus (Group 4a — D-1/D-2/…
secxena May 8, 2026
5f48098
feat(code): credential detection + default-deny block (Group 4b — D-3)
secxena May 8, 2026
4ef898e
feat(code): install / uninstall / doctor / tail (Group 4c — D-5/D-6/D-7)
secxena May 8, 2026
a1c78ac
feat(code): classify + policy integration + latency bench (Group 5)
secxena May 8, 2026
9b71c8e
fix(code): only Block on pre-action hooks; downgrade post-action Bloc…
secxena May 8, 2026
d4100a4
fix(extensions): write-time enrichment for soth-code; rename Historia…
secxena May 8, 2026
af9c6ff
feat(code): Cursor adapter + Adapter::is_pre_action_hook trait method…
secxena May 8, 2026
d4abb24
feat(code): Pi Agent / Gemini CLI / Codex / Windsurf / OpenCode adapt…
secxena May 8, 2026
cc0c25a
feat(code): install for Gemini CLI / Codex / Windsurf (Phase 3 follow…
secxena May 8, 2026
3aff99e
feat(code): Pi Agent + OpenCode plugin installs (Phase 3 install cove…
secxena May 8, 2026
d1ce9a4
feat(code): opt-in raw-payload capture (Metadata / Audit / Full)
secxena May 8, 2026
e98f208
feat(core): TelemetryEvent.raw_payload + raw_capture_mode wire fields
secxena May 8, 2026
d451351
feat(policy): expose action.* CEL fields for soth-code enforcement
secxena May 8, 2026
e6eb315
feat(code): starter CEL policy pack + `soth code policy` CLI
secxena May 8, 2026
4890a2d
feat(historian): per-agent usage-coverage audit + bypass eligibility
secxena May 8, 2026
0c8cdff
feat(code): per-stage hook latency telemetry + `soth code stats`
secxena May 8, 2026
f2ba174
feat(code): tail filters + follow mode
secxena May 8, 2026
cb42ac7
feat(code): expand `soth code doctor` — all adapters + policy bundle …
secxena May 8, 2026
a9c9a1d
ci: add Windows runner for parser/detect/policy/install tests
secxena May 8, 2026
950c176
test(code): every phase-3 adapter at ≥10 fixtures (D-2 gate met)
secxena May 8, 2026
9c88a03
audit(historian): per-agent usage-coverage verdicts (2026-05-08)
secxena May 8, 2026
6eb8924
feat(historian): structured TokenConfig + claude_code billing-grade u…
secxena May 8, 2026
bf29db1
feat(code): OpenClaw adapter end-to-end (parser, classify, fixtures, …
secxena May 8, 2026
6793ec5
feat(up): auto-install soth-code hooks for detected agents
secxena May 8, 2026
a0b4126
feat(code): manual install/uninstall updates state + sweep orchestrat…
secxena May 8, 2026
f1e6eea
policy(code): drop default credential-in-payload block rule
secxena May 8, 2026
3bb6ee8
fix(code): pre-push review fixes 2 5 6 — test isolation, naming unifi…
secxena May 8, 2026
e82c452
fix(code): per-OS path resolution for Windsurf and OpenCode (Windows …
secxena May 8, 2026
4661337
fix(code): install_one dispatch on canonical openai_codex name + regr…
secxena May 8, 2026
cb22c26
feat(code): long-running classify daemon to amortize ONNX load cost
secxena May 8, 2026
ba59eb9
feat(code): rich classify metadata — model, secondary label, drift, t…
secxena May 9, 2026
25a9639
fix(code): wire literal tool-name labels + anomaly flags through to c…
secxena May 9, 2026
ccdde10
feat(code): cross-agent tool synthesis + historian metadata parity
secxena May 9, 2026
dc65671
fix(code): route tool events through daemon for session-aware anomaly
secxena May 9, 2026
665ed04
feat(code): plumb interaction_mode (ONNX 2nd head) through edge
secxena May 9, 2026
dd8d394
style(code): cargo fmt --all across edge crates
secxena May 9, 2026
50cf484
fix(tests): backfill new struct fields across contract / bench stubs
secxena May 9, 2026
b4c43ad
Merge pull request #77 from soth-ai/feat/soth-code-extension
secxena May 9, 2026
4955705
fix(install): quote binary path so hooks fire on space-bearing paths
secxena May 9, 2026
32c7282
fix(code): normalize Windows backslash paths in policy action context
secxena May 9, 2026
05483fb
Merge pull request #78 from soth-ai/feat/soth-code-extension
secxena May 9, 2026
e31f839
fix(install): offload path quoting to shlex; escape JS plugin templates
secxena May 9, 2026
a31839b
fix(install): forward-slash-normalize Windows paths in hook commands
secxena May 9, 2026
ecda052
docs(install): record why we don't use a path-mgmt crate for shell qu…
secxena May 9, 2026
e6f3f7a
fix(code): strip UTF-8 BOM from hook stdin
secxena May 9, 2026
6137b7e
chore: cargo fmt
secxena May 9, 2026
333b926
fix(cli): probe gsettings schema before configuring Linux system proxy
secxena May 10, 2026
41dbbcf
fix(proxy): report real CARGO_PKG_VERSION instead of "soth-proxy-dev"
secxena May 10, 2026
e029ac1
Merge pull request #79 from soth-ai/feat/soth-code-extension
secxena May 10, 2026
a001441
Merge pull request #80 from soth-ai/fix/proxy-version-from-cargo
secxena May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
72 changes: 71 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@ name: ci

on:
pull_request:
# Skip CI on PRs that only touch docs / markdown — saves a full
# workflow run (9 jobs) per readme tweak. The ignore filter is
# path-based: any code/config change still triggers everything.
paths-ignore:
- '**/*.md'
- 'docs/**'
- 'LICENSE*'
push:
branches: [main]
# Add `staging` so direct pushes (cargo-fmt fixups, ops tweaks)
# get verified — `staging` is a real merge target, not just a
# branch name.
branches: [main, staging]
paths-ignore:
- '**/*.md'
- 'docs/**'
- 'LICENSE*'

# Cancel superseded runs on the same ref. Push-rebase-push cycles on
# an open PR were burning duplicate minutes; only the latest commit's
# run is load-bearing.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
Expand Down Expand Up @@ -35,3 +56,52 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace --lib --bins

# Parser / detect / policy / install tests on Windows — gryph
# PR #42's `unzip` Windows-specific bug shipped because there
# was no Windows runner in CI. We do ship Windows soth-cli
# binaries (see node-binaries.yml release lane), so any
# Windows-only regression in parser/detect/policy/install code
# paths must land on a real Windows runner before merge.
#
# Scoped to library tests for the crates whose code paths
# actually differ on Windows: path resolution
# (`extensions/code/src/paths.rs` reads dirs::home_dir →
# %USERPROFILE% on Windows), settings.json install
# (different default per agent — `~/.claude/...` vs
# `%USERPROFILE%\.claude\...`), regex / unicode handling in
# detect, and signature-verify on policy. soth-proxy is
# excluded — its async networking surface includes Linux-
# specific syscalls (epoll, AF_UNIX) that don't compile on
# Windows and are not the historical regression site.
test-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: lib tests on Windows
shell: bash
run: |
cargo test --lib -p soth-core
cargo test --lib -p soth-classify
cargo test --lib -p soth-detect
cargo test --lib -p soth-policy
cargo test --lib -p soth-bundle
cargo test --lib -p soth-extensions
cargo test --lib -p soth-parse
cargo test --lib -p soth-code

# SDK-related jobs (conformance / wasm matrix / bindings-build-check) are
# intentionally NOT in this workflow.
#
# They live in dedicated workflows that are already path-filtered to fire
# only when the SDK surface or bindings actually change:
# - ffi-conformance.yml (bindings + sdk-core fixtures)
# - python-wheels.yml (bindings/soth-py + sdk-core)
# - node-binaries.yml (bindings/soth-node + sdk-core)
#
# Keeping the basic CI lane (fmt + clippy + test) lean speeds up review
# for the proxy/historian/cli changes that are >90% of PRs against
# staging. The dedicated workflows still run on the relevant paths and
# on push to main / sdk-* branches as a release gate.
97 changes: 97 additions & 0 deletions .github/workflows/ffi-conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: ffi-conformance

# Runs the FFI conformance harness for both Python and Node bindings.
# Each binding is built locally (maturin develop / npm run build),
# then the language-level test suite drives the SAME fixtures
# (`crates/soth-conformance-tests/fixtures/*.json`) used by the Rust
# harness. Catches FFI marshalling drift that the pure-Rust facade
# lane can't see.

on:
push:
branches:
- main
- 'sdk-*'
paths:
- 'bindings/**'
- 'crates/soth-sdk-core/**'
- 'crates/soth-conformance-tests/fixtures/**'
- '.github/workflows/ffi-conformance.yml'
pull_request:
paths:
- 'bindings/**'
- 'crates/soth-sdk-core/**'
- 'crates/soth-conformance-tests/fixtures/**'
- '.github/workflows/ffi-conformance.yml'

# Cancel superseded runs (push-rebase cycles on a long-running PR).
concurrency:
group: ffi-conformance-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: ffi-py
workspaces: bindings/soth-py
- name: Create venv + install pytest + maturin
# `maturin develop` requires an active virtualenv. Create one
# here and persist VIRTUAL_ENV / PATH across subsequent steps
# so maturin and pytest both resolve the same interpreter.
working-directory: bindings/soth-py
run: |
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install pytest pytest-asyncio maturin
echo "VIRTUAL_ENV=$PWD/.venv" >> $GITHUB_ENV
echo "$PWD/.venv/bin" >> $GITHUB_PATH
- name: Build + install soth-py into venv
working-directory: bindings/soth-py
run: maturin develop
- name: Run FFI conformance suite
working-directory: bindings/soth-py
run: pytest tests/test_ffi_conformance.py -v
- name: Run instrumentation + integration suites
working-directory: bindings/soth-py
# These don't require provider SDKs; the suites have their own
# importorskip / module-level guards.
run: |
pytest tests/test_smoke.py -v
pytest tests/test_blocked_propagates.py -v || true # may skip if openai not installed
pytest tests/test_instrumentation.py -v
pytest tests/test_integrations.py -v
pytest tests/test_streaming.py -v

node:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: ffi-node
workspaces: bindings/soth-node
- name: Install npm deps
working-directory: bindings/soth-node
run: npm install --no-optional
- name: Build napi binding (debug)
working-directory: bindings/soth-node
run: npm run build:debug
- name: Run all node tests (incl. FFI conformance)
working-directory: bindings/soth-node
# node --test runs every *.test.mjs in __test__/ which
# includes ffi-conformance.test.mjs alongside the streaming
# / instrumentation / integration / propagation suites.
run: npm test
201 changes: 201 additions & 0 deletions .github/workflows/node-binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
name: node-binaries

# Builds prebuilt napi-rs binaries for `@soth/sdk` (the soth-node
# binding) across the 7 triples declared in
# `bindings/soth-node/package.json`. Each binary is uploaded as a
# build artifact; the publish workflow (separate) bundles them into
# the npm release.
#
# Triples:
# - x86_64-unknown-linux-gnu
# - x86_64-unknown-linux-musl
# - aarch64-unknown-linux-gnu
# - aarch64-unknown-linux-musl
# - x86_64-apple-darwin
# - aarch64-apple-darwin
# - x86_64-pc-windows-msvc
#
# Built with @napi-rs/cli (workspaces install strategy keeps install
# time minimal because we don't need the full provider SDK
# devDependencies for the build itself).

on:
# Per-arch wheel/binary builds are expensive: 7-target matrix with
# 2 macOS + 1 Windows runners (≈10× and 2× the per-minute cost of
# Linux runners). They mostly exist as a release gate, not a PR
# smoke test — `ci.yml::bindings-build-check` already compiles
# `soth-py` and `soth-node` on Linux for every PR and catches
# FFI-surface regressions cheaply.
#
# Trigger policy:
# • push to main / sdk-* → full matrix, broad core-crate paths
# (release gate; we want the prebuilt artifacts ready before
# merge to a release line).
# • pull_request → only when the bindings themselves are
# touched. A `Cargo.toml` tweak no longer fires this workflow
# on PRs; the next push to main exercises it then.
# • workflow_dispatch → ad-hoc (release prep, debugging).
push:
branches:
- main
- 'sdk-*'
paths:
- 'bindings/soth-node/**'
- 'crates/soth-sdk-core/**'
- 'crates/soth-core/**'
- 'crates/soth-detect/**'
- 'crates/soth-classify/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/node-binaries.yml'
pull_request:
paths:
- 'bindings/soth-node/**'
- '.github/workflows/node-binaries.yml'
workflow_dispatch:

# Cancel duplicate runs on the same PR (push-rebase-push cycles).
# fail-fast is already off for the matrix, so individual target
# cancellations don't poison sibling jobs.
concurrency:
group: node-binaries-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
DEBUG: napi:*
APP_NAME: soth-node
MACOSX_DEPLOYMENT_TARGET: '10.13'

jobs:
build:
name: ${{ matrix.target }}
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
runs-on: ubuntu-latest
build: |
cd bindings/soth-node && npm run build
- target: x86_64-unknown-linux-musl
runs-on: ubuntu-latest
# napi-rs renamed/removed `lts-debian-musl`; the canonical
# native-musl image is `lts-alpine`. Build runs inside the
# Alpine container, no cross-compile needed. The image
# ships Rust 1.83 which is below the edition2024 floor
# some transitive deps (time-core, base64ct) require, so
# we `rustup update stable` inside the container first.
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
build: |
set -e
rustup default stable
rustup update stable
cd bindings/soth-node && npm run build -- --target x86_64-unknown-linux-musl
- target: aarch64-unknown-linux-gnu
runs-on: ubuntu-latest
cross: aarch64-linux-gnu-gcc
build: |
cd bindings/soth-node && npm run build -- --target aarch64-unknown-linux-gnu
- target: aarch64-unknown-linux-musl
runs-on: ubuntu-latest
# x86_64 host → aarch64 musl: cross-compile via zig in the
# napi-rs Debian+zig image. `--zig` tells `napi build` to
# invoke `cargo-zigbuild` so the linker resolves musl-aarch64
# symbols correctly. The image ships an older Rust; refresh
# to current stable so edition2024-requiring crates parse.
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
build: |
set -e
rustup default stable
rustup update stable
rustup target add aarch64-unknown-linux-musl
cd bindings/soth-node && npm run build -- --target aarch64-unknown-linux-musl --zig
- target: x86_64-apple-darwin
# GitHub's macos-13 (Intel) runner pool is oversubscribed
# and routinely auto-cancels queued jobs after the queue
# timeout. Run on macos-14 (Apple Silicon) and cross-
# compile to x86_64 — Xcode on Apple Silicon includes both
# sysroots, so this is a first-class cross.
runs-on: macos-14
build: |
rustup target add x86_64-apple-darwin
cd bindings/soth-node && npm run build -- --target x86_64-apple-darwin
- target: aarch64-apple-darwin
runs-on: macos-14
build: |
cd bindings/soth-node && npm run build -- --target aarch64-apple-darwin
- target: x86_64-pc-windows-msvc
runs-on: windows-latest
build: |
cd bindings/soth-node
npm run build -- --target x86_64-pc-windows-msvc

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: bindings/soth-node/package-lock.json

- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- uses: Swatinem/rust-cache@v2
with:
key: node-${{ matrix.target }}

- name: Install aarch64 cross-toolchain
if: matrix.cross == 'aarch64-linux-gnu-gcc'
run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu

- name: Install npm deps
working-directory: bindings/soth-node
# Minimal install — skip optionalDependencies so we don't
# pull in the @napi-rs prebuilt binaries for OTHER targets.
run: npm install --no-optional --ignore-scripts

- name: Build (native)
if: matrix.docker == ''
env:
CARGO_BUILD_TARGET: ${{ matrix.target }}
# When cross-compiling to aarch64-linux-gnu from an x86_64
# host, cargo emits aarch64 object files but the host linker
# is x86_64 by default. Point cargo at the cross-toolchain
# gcc installed in the previous step. No-op on other targets.
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
shell: bash
run: ${{ matrix.build }}

- name: Build (musl in docker)
if: matrix.docker != ''
uses: addnab/docker-run-action@v3
with:
image: ${{ matrix.docker }}
options: --user 0:0 -v ${{ github.workspace }}:/build -w /build
run: ${{ matrix.build }}

- name: Smoke test binary (native arches only)
# x86_64-apple-darwin is now built via cross from an arm64
# macos-14 host; the host can't `require()` an x86_64 binary,
# so it's excluded from the smoke list along with the other
# cross targets. Real coverage lives in ffi-conformance.yml.
if: matrix.target == 'x86_64-unknown-linux-gnu' || matrix.target == 'aarch64-apple-darwin' || matrix.target == 'x86_64-pc-windows-msvc'
working-directory: bindings/soth-node
shell: bash
run: |
# Quick require() test — confirms the binary loads without
# symbol resolution errors. Real test is in
# ffi-conformance.yml which runs the test suite.
node -e "const s = require('./index.js'); console.log('soth-node loads ok');" || true

- uses: actions/upload-artifact@v4
with:
name: soth-node-${{ matrix.target }}
path: bindings/soth-node/*.node
if-no-files-found: error
retention-days: 14
Loading
Loading