Skip to content

Commit 13974e9

Browse files
authored
Merge pull request #4 from PruvoNet/binaries
feat: ship native binaries via GitHub Releases
2 parents 2ffcf20 + 1b6a6e7 commit 13974e9

5 files changed

Lines changed: 5785 additions & 2109 deletions

File tree

.github/workflows/prebuild.yml

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
name: prebuild
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
tags: ['v*']
7+
pull_request:
8+
workflow_dispatch:
9+
10+
# Least-privilege default: matrix jobs only need to read the repo to clone.
11+
# The `release` job overrides this below with `contents: write` for upload.
12+
permissions:
13+
contents: read
14+
15+
# Node versions to produce prebuilds for. One .tar.gz per ABI per
16+
# (platform, arch, libc) combination is produced. On consumer install,
17+
# prebuild-install downloads only the asset matching the consumer's runtime.
18+
env:
19+
NODE_TARGETS: "24.15.0 26.1.0"
20+
21+
jobs:
22+
prebuild:
23+
name: ${{ matrix.id }}
24+
runs-on: ${{ matrix.runner }}
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
include:
29+
- id: darwin-x64
30+
runner: macos-13
31+
- id: darwin-arm64
32+
runner: macos-14
33+
- id: linux-x64-glibc
34+
runner: ubuntu-latest
35+
- id: linux-x64-musl
36+
runner: ubuntu-latest
37+
musl: true
38+
- id: linux-arm64-glibc
39+
runner: ubuntu-24.04-arm
40+
- id: linux-arm64-musl
41+
runner: ubuntu-24.04-arm
42+
musl: true
43+
- id: win32-x64
44+
runner: windows-latest
45+
46+
steps:
47+
- uses: actions/checkout@v5
48+
49+
- name: Set up Node (non-musl jobs)
50+
if: ${{ !matrix.musl }}
51+
uses: actions/setup-node@v5
52+
with:
53+
node-version: '24'
54+
55+
- name: Install npm deps (non-musl)
56+
if: ${{ !matrix.musl }}
57+
run: npm ci --ignore-scripts
58+
59+
- name: Build prebuilds for all target ABIs (non-musl)
60+
if: ${{ !matrix.musl }}
61+
shell: bash
62+
run: |
63+
set -euo pipefail
64+
args=""
65+
for v in $NODE_TARGETS; do
66+
args="$args -t $v"
67+
done
68+
# --strip removes debug symbols. --tag-libc tags linux assets with
69+
# glibc/musl so prebuild-install can request the right one on the
70+
# consumer side.
71+
npx prebuild $args --strip --tag-libc
72+
73+
# Musl path runs inside docker rather than using `container:`, because
74+
# GitHub Actions JavaScript actions can't run in Alpine containers on
75+
# ARM64 runners. Doing the whole build inside `docker run` sidesteps
76+
# that limitation and keeps the x64/arm64 musl steps identical.
77+
- name: Build musl prebuilds via Alpine docker
78+
if: ${{ matrix.musl }}
79+
shell: bash
80+
run: |
81+
set -euo pipefail
82+
docker run --rm \
83+
-v "$PWD":/work -w /work \
84+
-e NODE_TARGETS="$NODE_TARGETS" \
85+
node:24-alpine sh -c '
86+
set -e
87+
apk add --no-cache python3 make g++ git tar
88+
npm ci --ignore-scripts
89+
args=""
90+
for v in $NODE_TARGETS; do
91+
args="$args -t $v"
92+
done
93+
npx prebuild $args --strip --tag-libc
94+
'
95+
96+
- name: Verify each tarball contains a .node binary
97+
shell: bash
98+
run: |
99+
set -euo pipefail
100+
ls -la prebuilds/
101+
shopt -s nullglob
102+
tarballs=(prebuilds/*.tar.gz)
103+
if [ ${#tarballs[@]} -eq 0 ]; then
104+
echo "ERROR: no prebuilds produced" >&2
105+
exit 1
106+
fi
107+
for f in "${tarballs[@]}"; do
108+
echo "--- $f ---"
109+
tar -tzf "$f"
110+
tar -tzf "$f" | grep -q '\.node$' || { echo "ERROR: no .node file in $f" >&2; exit 1; }
111+
done
112+
113+
- uses: actions/upload-artifact@v4
114+
with:
115+
name: prebuilds-${{ matrix.id }}
116+
path: prebuilds/*.tar.gz
117+
if-no-files-found: error
118+
retention-days: 30
119+
120+
# Gate the release on tag being reachable from master. If the tag was
121+
# pushed from a feature branch (or any non-master commit), this job
122+
# outputs on_master=false and the release job is skipped (not failed).
123+
gate-release:
124+
name: gate release on master ancestry
125+
needs: prebuild
126+
if: startsWith(github.ref, 'refs/tags/v')
127+
runs-on: ubuntu-latest
128+
outputs:
129+
on_master: ${{ steps.check.outputs.on_master }}
130+
steps:
131+
- uses: actions/checkout@v5
132+
with:
133+
fetch-depth: 0
134+
- id: check
135+
run: |
136+
set -euo pipefail
137+
git fetch origin master
138+
if git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then
139+
echo "Tag $GITHUB_REF_NAME (commit $GITHUB_SHA) is on master — release will publish"
140+
echo "on_master=true" >> "$GITHUB_OUTPUT"
141+
else
142+
echo "Tag $GITHUB_REF_NAME (commit $GITHUB_SHA) is NOT on master — release will be skipped"
143+
echo "Merge your PR to master first, then re-tag the merge commit."
144+
echo "on_master=false" >> "$GITHUB_OUTPUT"
145+
fi
146+
147+
release:
148+
name: attach prebuilds to GitHub Release
149+
needs: [prebuild, gate-release]
150+
if: needs.gate-release.outputs.on_master == 'true'
151+
runs-on: ubuntu-latest
152+
permissions:
153+
contents: write
154+
steps:
155+
- uses: actions/checkout@v5
156+
157+
- name: Download all prebuild artifacts
158+
uses: actions/download-artifact@v4
159+
with:
160+
pattern: prebuilds-*
161+
path: artifacts
162+
merge-multiple: true
163+
164+
- name: List downloaded assets
165+
run: ls -la artifacts/
166+
167+
- name: Create / update Release and upload assets
168+
uses: softprops/action-gh-release@v2
169+
with:
170+
# Auto-uses the tag from github.ref. Idempotent: re-runs on the same
171+
# tag will update the existing release and overwrite asset names
172+
# that collide.
173+
files: artifacts/*.tar.gz
174+
generate_release_notes: true
175+
fail_on_unmatched_files: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
build/
22
node_modules/
3+
prebuilds/
34

45
!.editorconfig
56
!.gitignore

0 commit comments

Comments
 (0)