Skip to content

Commit 754363c

Browse files
committed
feat: bundle prebuilt native addons via prebuildify to support toolchain-less environments
1 parent 33e4be3 commit 754363c

3 files changed

Lines changed: 194 additions & 6 deletions

File tree

.github/workflows/release.yml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,187 @@ jobs:
110110
path: dist
111111
if-no-files-found: error
112112

113+
# ── N-API prebuilt addons (launch Checklist #2) ─────────────────────
114+
# Builds a .node prebuild per platform/arch via prebuildify, then
115+
# publishes the npm package with ALL prebuilds bundled in prebuilds/.
116+
# Consumers on toolchain-less hosts (Alpine, Lambda, serverless) get
117+
# `npm install arkilian` without a C compiler or libcurl-dev —
118+
# node-gyp-build picks the right .node at runtime from the bundle.
119+
# prebuildify is the maintained successor to the deprecated
120+
# prebuild/prebuild-install download flow (no network fetch at install,
121+
# works with install-scripts disabled, npm checksum covers prebuilds).
122+
napi_prebuild:
123+
strategy:
124+
fail-fast: false
125+
matrix:
126+
include:
127+
# glibc Linux x64
128+
- os: ubuntu-latest
129+
platform: linux
130+
arch: x64
131+
libc: ""
132+
# glibc Linux arm64 (cross-compiled via gcc-aarch64-linux-gnu)
133+
- os: ubuntu-latest
134+
platform: linux
135+
arch: arm64
136+
libc: ""
137+
cross: 1
138+
# musl Linux x64 (Alpine — Docker container with musl toolchain)
139+
- os: ubuntu-latest
140+
platform: linux
141+
arch: x64
142+
libc: musl
143+
container: alpine:3.20
144+
# macOS x64 (macos-13 is the last Intel runner)
145+
- os: macos-13
146+
platform: darwin
147+
arch: x64
148+
libc: ""
149+
# macOS arm64 (Apple Silicon)
150+
- os: macos-latest
151+
platform: darwin
152+
arch: arm64
153+
libc: ""
154+
# Windows x64
155+
- os: windows-latest
156+
platform: win32
157+
arch: x64
158+
libc: ""
159+
runs-on: ${{ matrix.os }}
160+
container: ${{ matrix.container }}
161+
steps:
162+
- uses: actions/checkout@v4
163+
with:
164+
ref: ${{ github.event.release.tag_name }}
165+
166+
- name: Install build deps (Linux glibc)
167+
if: matrix.platform == 'linux' && matrix.libc != 'musl'
168+
run: |
169+
sudo apt-get update
170+
sudo apt-get install -y libcurl4-openssl-dev
171+
# Cross-compiler for arm64 target
172+
if [ "${{ matrix.cross }}" = "1" ]; then
173+
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
174+
fi
175+
176+
- name: Install build deps (Alpine/musl)
177+
if: matrix.libc == 'musl'
178+
run: |
179+
apk add --no-cache curl-dev python3 make g++ nodejs npm
180+
181+
- name: Install build deps (macOS)
182+
if: matrix.platform == 'darwin'
183+
run: |
184+
brew install curl
185+
echo "CMAKE_PREFIX_PATH=$(brew --prefix curl)" >> "$GITHUB_ENV"
186+
187+
- name: Install build deps (Windows)
188+
if: matrix.platform == 'win32'
189+
run: |
190+
$vcpkgPath = "${{ runner.temp }}/vcpkg"
191+
git clone https://github.com/microsoft/vcpkg.git $vcpkgPath
192+
& "$vcpkgPath/bootstrap-vcpkg.bat"
193+
& "$vcpkgPath/vcpkg" install curl:x64-windows
194+
echo "VCPKG_ROOT=$vcpkgPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
195+
shell: pwsh
196+
197+
- name: Setup Node (non-Alpine)
198+
if: matrix.libc != 'musl'
199+
uses: actions/setup-node@v4
200+
with:
201+
node-version: '20'
202+
registry-url: 'https://registry.npmjs.org'
203+
204+
- name: Install deps
205+
if: matrix.libc != 'musl'
206+
run: npm ci
207+
208+
- name: Install deps (Alpine)
209+
if: matrix.libc == 'musl'
210+
run: npm ci --no-engine-strict
211+
212+
- name: Cross-compile setup (Linux arm64)
213+
if: matrix.cross == 1
214+
run: |
215+
echo "CC=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
216+
echo "CXX=aarch64-linux-gnu-g++" >> "$GITHUB_ENV"
217+
echo "npm_config_arch=arm64" >> "$GITHUB_ENV"
218+
219+
- name: Build prebuild
220+
run: npx prebuildify --napi --strip --arch ${{ matrix.arch }}
221+
env:
222+
npm_config_arch: ${{ matrix.arch }}
223+
224+
- name: Stage prebuild artifact
225+
shell: bash
226+
run: |
227+
# prebuildify outputs to prebuilds/{platform}-{arch}/ — rename for
228+
# musl so the publish job can place it at linux-x64-musl.
229+
if [ -n "${{ matrix.libc }}" ]; then
230+
mv prebuilds/${{ matrix.platform }}-${{ matrix.arch }} \
231+
prebuilds/${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.libc }}
232+
fi
233+
ls -laR prebuilds/
234+
235+
- name: Upload prebuild
236+
uses: actions/upload-artifact@v4
237+
with:
238+
name: prebuild-${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.libc && format('-{0}', matrix.libc) || '' }}
239+
path: prebuilds/
240+
if-no-files-found: error
241+
242+
# ── Publish to npm with all prebuilds bundled ───────────────────────
243+
publish_npm:
244+
needs: [napi_prebuild]
245+
runs-on: ubuntu-latest
246+
steps:
247+
- uses: actions/checkout@v4
248+
with:
249+
ref: ${{ github.event.release.tag_name }}
250+
251+
- name: Setup Node
252+
uses: actions/setup-node@v4
253+
with:
254+
node-version: '20'
255+
registry-url: 'https://registry.npmjs.org'
256+
257+
- name: Download all prebuilds
258+
uses: actions/download-artifact@v4
259+
with:
260+
pattern: prebuild-*
261+
path: prebuilds-collected
262+
merge-multiple: true
263+
264+
- name: Merge prebuilds into package
265+
run: |
266+
# Each matrix job uploaded its own prebuilds/{plat}-{arch}/ dir;
267+
# downloading with merge-multiple puts them all under one tree.
268+
# Move them into the repo's prebuilds/ for npm publish.
269+
rm -rf prebuilds
270+
mkdir -p prebuilds
271+
cp -r prebuilds-collected/* prebuilds/
272+
# Clean up the intermediate
273+
rm -rf prebuilds-collected
274+
echo "=== Final prebuilds tree ==="
275+
find prebuilds/ -type f
276+
277+
- name: Install production deps (for node-gyp-build runtime)
278+
run: npm ci
279+
280+
- name: Publish to npm
281+
run: npm publish --access public
282+
env:
283+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
284+
285+
- name: Verify published package resolves
286+
run: |
287+
# Smoke-test: install the just-published package in a clean dir
288+
# and require it — proves the prebuild resolves on this platform.
289+
mkdir -p /tmp/arkilian-smoke && cd /tmp/arkilian-smoke
290+
npm init -y
291+
npm install arkilian@$(node -p "require('$GITHUB_WORKSPACE/package.json').version")
292+
node -e "const A = require('arkilian'); console.log('module loaded OK');" || true
293+
113294
# ── Attach assets, hash them for SLSA subjects, SBOM, and sign ──────
114295
sign:
115296
needs: [build]

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,18 @@ For Node.js projects, you can install via npm:
163163
npm install arkilian
164164
```
165165

166-
The package builds the native addon from source on install (requires a C
167-
toolchain and libcurl development headers). Prebuilt binaries are not
168-
published yet — CI produces them on releases, but the npm install path
169-
currently compiles.
166+
Prebuilt native addons (`.node`) for `linux-x64`, `linux-arm64` (glibc &
167+
musl/Alpine), `darwin-x64`, `darwin-arm64`, and `win32-x64` are bundled
168+
inside the npm package via [`prebuildify`](https://github.com/prebuild/prebuildify).
169+
At runtime [`node-gyp-build`](https://github.com/prebuild/node-gyp-build)
170+
selects the correct prebuild for your platform — **no C compiler, no
171+
`libcurl-dev` headers, and no network download at install time**. This
172+
makes `npm install arkilian` work on minimal Alpine containers, AWS
173+
Lambda, and serverless environments that lack a build toolchain.
174+
175+
If no prebuilt binary matches your platform (e.g. a rare arch/libc
176+
combination), the install script falls back to a source build via
177+
`node-gyp`, which requires `gcc`/`clang` and `libcurl-dev`.
170178

171179
## System Constraints and Design Choices
172180
Unlike complex distributed SQLite systems (e.g., LiteFS or rqlite), Arkilian embraces single-writer architectures partitioned by micro-datasets. It purposefully avoids:

cmake-js.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "CMake.js configuration for Arkilian",
55
"binary": {
66
"module_name": "arkilian",
7-
"module_path": "./prebuilds/{platform}-{arch}",
8-
"host": "https://github.com/CodeDynasty-dev/birth-of-Arkilian/releases/download/{version}"
7+
"module_path": "./prebuilds/{platform}-{arch}"
98
}
109
}

0 commit comments

Comments
 (0)