1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
name: Node publish
# Builds the prebuilt addon for every supported platform, then publishes the
# thin `infino` package plus its per-platform binary packages (`infino-<triple>`)
# to npm.
#
# Triggered manually. The default run is a dry run: it packs and validates the
# main package and every platform package without uploading anything. Re-run
# with dry_run unchecked to publish for real. A real run needs the NPM_TOKEN
# secret, with publish rights to the packages under the infino-ai org.
on:
workflow_dispatch:
inputs:
dry_run:
description: "Validate only — pack everything, publish nothing"
type: boolean
default: true
env:
CARGO_TERM_COLOR: always
# The binding builds infino as a normal dependency; don't fail on a
# transitive-dependency deprecation warning (same as the CI node jobs).
RUSTFLAGS: ""
jobs:
build:
name: Build addon (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# glibc + macOS build natively, one runner per target (`make node-build`
# builds for the host). musl (Alpine) has no native GitHub runner, so it
# builds inside the napi-rs Alpine image where the system `cc` is musl —
# the crate has C-based deps (e.g. zstd), so a native musl toolchain is
# simpler and more reliable than cross-compiling from glibc.
matrix:
include:
-
-
-
-
-
-
steps:
- uses: actions/checkout@v4
- name: Free disk space
if: runner.os == 'Linux'
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
# --- native build (glibc / macOS) ---
- uses: dtolnay/rust-toolchain@stable
if: ${{ !matrix.musl }}
- uses: actions/setup-node@v4
if: ${{ !matrix.musl }}
with:
node-version: "20"
- uses: Swatinem/rust-cache@v2
if: ${{ !matrix.musl }}
with:
workspaces: infino-node
- name: Build addon (native)
if: ${{ !matrix.musl }}
run: make node-build
# Smoke-test the AS-SHIPPED package on this platform: pack the tarballs,
# install them into a throwaway project, and run a real query — proving the
# loader resolves the per-platform binary from a clean install (not the
# sibling build `npm test` uses). Reuses the build above (no rebuild).
- name: Verify packaged binary (native)
if: ${{ !matrix.musl }}
run: make node-verify
# --- musl build (Alpine, via the napi-rs builder image; cc is musl) ---
# Runs on the host runner (so the disk-free step above still applies) but
# compiles inside Alpine, where the target arch matches the runner arch —
# so this is a native musl build, not a cross-compile. Same install→load→
# query smoke-test as the native legs, run inside the container (bash is
# not in the base image, so add it for verify-pack.sh).
- name: Build addon (musl, in Alpine) + verify packaged binary
if: ${{ matrix.musl }}
run: |
docker run --rm -v "$PWD:/build" -w /build/infino-node \
ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine \
sh -c "apk add --no-cache bash >/dev/null && \
rustup target add ${{ matrix.musl }} && \
npm install --ignore-scripts && \
npx napi build --platform --release --strip --target ${{ matrix.musl }} --js native.js --dts native.d.ts infino && \
npx tsc && \
bash ./scripts/verify-pack.sh"
- name: Upload addon
uses: actions/upload-artifact@v4
with:
name: addon-${{ matrix.target }}
path: infino-node/infino/*.node
if-no-files-found: error
# The JS loader and compiled wrapper are identical across platforms;
# upload them once so the publish job needs no Rust build.
- name: Upload JS bindings
if: matrix.target == 'linux-x64-gnu'
uses: actions/upload-artifact@v4
with:
name: js-bindings
path: |
infino-node/infino/native.js
infino-node/infino/native.d.ts
infino-node/infino/index.js
infino-node/infino/index.d.ts
if-no-files-found: error
publish:
name: Publish to npm
needs: build
runs-on: ubuntu-latest
# OIDC token for npm provenance — the "built from this repo at this commit"
# attestation shown on npmjs.org. A real supply-chain trust signal at launch.
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
# All platform binaries land flattened into infino-node/artifacts/;
# `napi artifacts` matches each by the triple in its filename.
- name: Download platform addons
uses: actions/download-artifact@v4
with:
pattern: addon-*
path: infino-node/artifacts
merge-multiple: true
- name: Download JS bindings
uses: actions/download-artifact@v4
with:
name: js-bindings
path: infino-node/infino
- name: Install tooling
run: cd infino-node && npm install --ignore-scripts
# npm/ isn't committed — generate the per-platform package dirs from
# package.json (triples, version, description) before staging binaries.
- name: Generate platform package dirs
run: cd infino-node && npx napi create-npm-dir -t .
# Copy each binary into its per-platform package under npm/<triple>/.
- name: Stage binaries into platform packages
run: cd infino-node && npm run artifacts
- name: Validate (dry run)
if: ${{ inputs.dry_run }}
run: |
cd infino-node
npx napi prepublish -t npm --skip-gh-release --dry-run
npm publish --dry-run --tag latest --access public
# Publishes the per-platform packages and pins them in the main
# package's optionalDependencies, then publishes the main package.
# `--tag latest` is required (not just default): the name's history
# includes a higher version, so npm won't move the latest tag onto
# 0.1.0 implicitly. Passing it explicitly sets latest to the engine.
- name: Publish to npm
if: ${{ !inputs.dry_run }}
run: |
cd infino-node
npx napi prepublish -t npm --skip-gh-release
npm publish --tag latest --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# Emit provenance for the main package AND every per-platform package
# (napi's prepublish shells out to `npm publish`, which honors this).
NPM_CONFIG_PROVENANCE: "true"