Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions .github/workflows/prebuild.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: prebuild

on:
push:
branches: [master, main]
tags: ['v*']
pull_request:
workflow_dispatch:

# Least-privilege default: matrix jobs only need to read the repo to clone.
# The `release` job overrides this below with `contents: write` for upload.
permissions:
contents: read

# Node versions to produce prebuilds for. One .tar.gz per ABI per
# (platform, arch, libc) combination is produced. On consumer install,
# prebuild-install downloads only the asset matching the consumer's runtime.
env:
NODE_TARGETS: "24.15.0 26.1.0"

jobs:
prebuild:
name: ${{ matrix.id }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- id: darwin-x64
runner: macos-13
- id: darwin-arm64
runner: macos-14
- id: linux-x64-glibc
runner: ubuntu-latest
- id: linux-x64-musl
runner: ubuntu-latest
musl: true
- id: linux-arm64-glibc
runner: ubuntu-24.04-arm
- id: linux-arm64-musl
runner: ubuntu-24.04-arm
musl: true
- id: win32-x64
runner: windows-latest

steps:
- uses: actions/checkout@v5

- name: Set up Node (non-musl jobs)
if: ${{ !matrix.musl }}
uses: actions/setup-node@v5
with:
node-version: '24'

- name: Install npm deps (non-musl)
if: ${{ !matrix.musl }}
run: npm ci --ignore-scripts

- name: Build prebuilds for all target ABIs (non-musl)
if: ${{ !matrix.musl }}
shell: bash
run: |
set -euo pipefail
args=""
for v in $NODE_TARGETS; do
args="$args -t $v"
done
# --strip removes debug symbols. --tag-libc tags linux assets with
# glibc/musl so prebuild-install can request the right one on the
# consumer side.
npx prebuild $args --strip --tag-libc

# Musl path runs inside docker rather than using `container:`, because
# GitHub Actions JavaScript actions can't run in Alpine containers on
# ARM64 runners. Doing the whole build inside `docker run` sidesteps
# that limitation and keeps the x64/arm64 musl steps identical.
- name: Build musl prebuilds via Alpine docker
if: ${{ matrix.musl }}
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$PWD":/work -w /work \
-e NODE_TARGETS="$NODE_TARGETS" \
node:24-alpine sh -c '
set -e
apk add --no-cache python3 make g++ git tar
npm ci --ignore-scripts
args=""
for v in $NODE_TARGETS; do
args="$args -t $v"
done
npx prebuild $args --strip --tag-libc
'

- name: Verify each tarball contains a .node binary
shell: bash
run: |
set -euo pipefail
ls -la prebuilds/
shopt -s nullglob
tarballs=(prebuilds/*.tar.gz)
if [ ${#tarballs[@]} -eq 0 ]; then
echo "ERROR: no prebuilds produced" >&2
exit 1
fi
for f in "${tarballs[@]}"; do
echo "--- $f ---"
tar -tzf "$f"
tar -tzf "$f" | grep -q '\.node$' || { echo "ERROR: no .node file in $f" >&2; exit 1; }
done

- uses: actions/upload-artifact@v4
with:
name: prebuilds-${{ matrix.id }}
path: prebuilds/*.tar.gz
if-no-files-found: error
retention-days: 30

# Gate the release on tag being reachable from master. If the tag was
# pushed from a feature branch (or any non-master commit), this job
# outputs on_master=false and the release job is skipped (not failed).
gate-release:
name: gate release on master ancestry
needs: prebuild
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
outputs:
on_master: ${{ steps.check.outputs.on_master }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: check
run: |
set -euo pipefail
git fetch origin master
if git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then
echo "Tag $GITHUB_REF_NAME (commit $GITHUB_SHA) is on master — release will publish"
echo "on_master=true" >> "$GITHUB_OUTPUT"
else
echo "Tag $GITHUB_REF_NAME (commit $GITHUB_SHA) is NOT on master — release will be skipped"
echo "Merge your PR to master first, then re-tag the merge commit."
echo "on_master=false" >> "$GITHUB_OUTPUT"
fi

release:
name: attach prebuilds to GitHub Release
needs: [prebuild, gate-release]
if: needs.gate-release.outputs.on_master == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v5

- name: Download all prebuild artifacts
uses: actions/download-artifact@v4
with:
pattern: prebuilds-*
path: artifacts
merge-multiple: true

- name: List downloaded assets
run: ls -la artifacts/

- name: Create / update Release and upload assets
uses: softprops/action-gh-release@v2
with:
# Auto-uses the tag from github.ref. Idempotent: re-runs on the same
# tag will update the existing release and overwrite asset names
# that collide.
files: artifacts/*.tar.gz
generate_release_notes: true
fail_on_unmatched_files: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
build/
node_modules/
prebuilds/

!.editorconfig
!.gitignore
Expand Down
Loading
Loading