Skip to content

Build Node.js from Source #33

Build Node.js from Source

Build Node.js from Source #33

Workflow file for this run

name: Build Node.js from Source
on:
workflow_dispatch:
inputs:
linux-x64:
description: 'Build Linux x64'
type: boolean
default: true
linux-arm64:
description: 'Build Linux ARM64'
type: boolean
default: true
macos-x64:
description: 'Build macOS x64'
type: boolean
default: true
macos-arm64:
description: 'Build macOS ARM64'
type: boolean
default: true
windows-x64:
description: 'Build Windows x64'
type: boolean
default: true
windows-arm64:
description: 'Build Windows ARM64'
type: boolean
default: true
env:
ARTIFACT_RETENTION_DAYS: 1
NODE_TAG: v24.15.0
GIT_AUTHOR_NAME: github-actions[bot]
GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
jobs:
# ─────────────────────────────────────────────
# Linux x64
# ─────────────────────────────────────────────
build-linux-x64:
if: inputs.linux-x64
runs-on: ubuntu-22.04
steps:
- name: Set artifact name
run: |
ARTIFACT_NAME="node-$NODE_TAG-linux-x64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
echo "ARCHIVE_NAME=$ARTIFACT_NAME.tar.gz" >> $GITHUB_ENV
- name: Install build dependencies
run: |
sudo apt-get update -q
sudo apt-get install -y -q \
python3 g++ make ninja-build git \
libssl-dev pkg-config \
xz-utils curl ca-certificates
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$NODE_TAG" \
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
run: ../repo/script/git-import-patches ../repo/patches
- name: Configure
working-directory: node-src
run: |
# --prefix: install root inside the staging directory
# --tag: cleared so the version string is clean (e.g. v24.14.1, not v24.14.1-custom)
# --ninja: use Ninja instead of Make for faster incremental builds
# --experimental-enable-pointer-compression: compress V8 heap pointers from 8 to 4 bytes, reducing heap size
# --experimental-pointer-compression-shared-cage: share the pointer compression cage across Isolates in the same process
# --without-siphash: disable SipHash for V8 hash seeds, using a simpler alternative
./configure \
--prefix=/usr/local/$ARTIFACT_NAME \
--tag="" \
--ninja \
--experimental-enable-pointer-compression \
--experimental-pointer-compression-shared-cage \
--without-siphash
- name: Build (this takes 20–40 min)
working-directory: node-src
run: make -j4
- name: Smoke test
working-directory: node-src
run: ./out/Release/node --version
- name: Install into staging directory
working-directory: node-src
run: make install DESTDIR=$PWD/../stage
- name: Smoke test npm
run: ../stage/usr/local/$ARTIFACT_NAME/bin/npm --version
working-directory: node-src
- name: Package tarball (full install tree)
run: |
tar czf "$ARCHIVE_NAME" -C stage/usr/local $ARTIFACT_NAME
echo "Archive size: $(du -sh $ARCHIVE_NAME | cut -f1)"
echo "Top-level entries:"
tar tzf "$ARCHIVE_NAME" | cut -d/ -f1-2 | sort -u | head -20
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARCHIVE_NAME }}
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# Linux ARM64 (GitHub-hosted)
# ─────────────────────────────────────────────
build-linux-arm64:
if: inputs.linux-arm64
runs-on: ubuntu-22.04-arm
steps:
- name: Set artifact name
run: |
ARTIFACT_NAME="node-$NODE_TAG-linux-arm64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
echo "ARCHIVE_NAME=$ARTIFACT_NAME.tar.gz" >> $GITHUB_ENV
- name: Install build dependencies
run: |
sudo apt-get update -q
sudo apt-get install -y -q \
python3 g++-12 make ninja-build git \
libssl-dev pkg-config \
xz-utils curl ca-certificates
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$NODE_TAG" \
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
run: ../repo/script/git-import-patches ../repo/patches
- name: Configure
working-directory: node-src
env:
CC: gcc-12
CXX: g++-12
run: |
# --prefix: install root inside the staging directory
# --ninja: use Ninja instead of Make for faster incremental builds
# --experimental-enable-pointer-compression: compress V8 heap pointers from 8 to 4 bytes, reducing heap size
# --experimental-pointer-compression-shared-cage: share the pointer compression cage across Isolates in the same process
# --without-siphash: disable SipHash for V8 hash seeds, using a simpler alternative
./configure \
--prefix=/usr/local/$ARTIFACT_NAME \
--ninja \
--experimental-enable-pointer-compression \
--experimental-pointer-compression-shared-cage \
--without-siphash
- name: Build
working-directory: node-src
env:
CC: gcc-12
CXX: g++-12
run: make -j4
- name: Smoke test
working-directory: node-src
run: ./out/Release/node --version
- name: Install into staging directory
working-directory: node-src
run: make install DESTDIR=$PWD/../stage
- name: Smoke test npm
working-directory: node-src
run: ../stage/usr/local/$ARTIFACT_NAME/bin/npm --version
- name: Package tarball (full install tree)
run: |
tar czf "$ARCHIVE_NAME" -C stage/usr/local $ARTIFACT_NAME
echo "Archive size: $(du -sh $ARCHIVE_NAME | cut -f1)"
echo "Top-level entries:"
tar tzf "$ARCHIVE_NAME" | cut -d/ -f1-2 | sort -u | head -20
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARCHIVE_NAME }}
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# macOS x64
# ─────────────────────────────────────────────
build-macos-x64:
if: inputs.macos-x64
runs-on: macos-15-intel # last Intel runner
steps:
- name: Set artifact name
run: |
ARTIFACT_NAME="node-$NODE_TAG-darwin-x64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
echo "ARCHIVE_NAME=$ARTIFACT_NAME.tar.gz" >> $GITHUB_ENV
- name: Install build dependencies
run: brew install python3 openssl pkg-config ninja
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$NODE_TAG" \
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
run: ../repo/script/git-import-patches ../repo/patches
- name: Configure
working-directory: node-src
run: |
# --prefix: install root inside the staging directory
# --openssl-use-def-ca-store: use the macOS system Keychain for CA certificates instead of the bundled Mozilla store
# --ninja: use Ninja instead of Make for faster incremental builds
# --experimental-enable-pointer-compression: compress V8 heap pointers from 8 to 4 bytes, reducing heap size
# --experimental-pointer-compression-shared-cage: share the pointer compression cage across Isolates in the same process
# --without-siphash: disable SipHash for V8 hash seeds, using a simpler alternative
./configure \
--prefix=/usr/local/$ARTIFACT_NAME \
--openssl-use-def-ca-store \
--ninja \
--experimental-enable-pointer-compression \
--experimental-pointer-compression-shared-cage \
--without-siphash
- name: Build
working-directory: node-src
run: make
- name: Smoke test
working-directory: node-src
run: ./out/Release/node --version
- name: Install into staging directory
working-directory: node-src
run: make install DESTDIR=$PWD/../stage
- name: Smoke test npm
working-directory: node-src
run: ../stage/usr/local/$ARTIFACT_NAME/bin/npm --version
- name: Package tarball (full install tree)
run: |
tar czf "$ARCHIVE_NAME" -C stage/usr/local $ARTIFACT_NAME
echo "Archive size: $(du -sh $ARCHIVE_NAME | cut -f1)"
echo "Top-level entries:"
tar tzf "$ARCHIVE_NAME" | cut -d/ -f1-2 | sort -u | head -20
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARCHIVE_NAME }}
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# macOS ARM64 (Apple Silicon)
# ─────────────────────────────────────────────
build-macos-arm64:
if: inputs.macos-arm64
runs-on: macos-15 # M1/M2 runner — ships with Xcode 16 (Clang 16+)
steps:
- name: Set artifact name
run: |
ARTIFACT_NAME="node-$NODE_TAG-darwin-arm64"
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
echo "ARCHIVE_NAME=$ARTIFACT_NAME.tar.gz" >> $GITHUB_ENV
- name: Select Xcode 16
# Ensures Clang 16+ is active — required by V8's C++20 TypeIndex cast
# in deps/v8/src/wasm/value-type.h (fails on Clang 15 / Xcode 15)
run: |
sudo xcode-select --switch /Applications/Xcode_16.4.app/Contents/Developer
echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV
- name: Verify Clang version (must be 16+)
run: clang++ --version
- name: Install build dependencies
run: brew install python3 openssl pkg-config ninja
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$NODE_TAG" \
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
run: ../repo/script/git-import-patches ../repo/patches
- name: Configure
working-directory: node-src
env:
# Point the build system at the Xcode 16 toolchain explicitly
CC: /Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
CXX: /Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
run: |
# --prefix: install root inside the staging directory
# --openssl-use-def-ca-store: use the macOS system Keychain for CA certificates instead of the bundled Mozilla store
# --ninja: use Ninja instead of Make for faster incremental builds
# --experimental-enable-pointer-compression: compress V8 heap pointers from 8 to 4 bytes, reducing heap size
# --experimental-pointer-compression-shared-cage: share the pointer compression cage across Isolates in the same process
# --without-siphash: disable SipHash for V8 hash seeds, using a simpler alternative
./configure \
--prefix=/usr/local/$ARTIFACT_NAME \
--openssl-use-def-ca-store \
--ninja \
--experimental-enable-pointer-compression \
--experimental-pointer-compression-shared-cage \
--without-siphash
- name: Build
working-directory: node-src
env:
CC: /Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
CXX: /Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
run: make
- name: Smoke test
working-directory: node-src
run: ./out/Release/node --version
- name: Install into staging directory
working-directory: node-src
run: make install DESTDIR=$PWD/../stage
- name: Smoke test npm
working-directory: node-src
run: ../stage/usr/local/$ARTIFACT_NAME/bin/npm --version
- name: Package tarball (full install tree)
run: |
tar czf "$ARCHIVE_NAME" -C stage/usr/local $ARTIFACT_NAME
echo "Archive size: $(du -sh $ARCHIVE_NAME | cut -f1)"
echo "Top-level entries:"
tar tzf "$ARCHIVE_NAME" | cut -d/ -f1-2 | sort -u | head -20
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARCHIVE_NAME }}
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# Windows x64
# ─────────────────────────────────────────────
build-windows-x64:
if: inputs.windows-x64
runs-on: windows-2025
steps:
- name: Install NASM (required by OpenSSL)
run: choco install nasm -y
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$env:NODE_TAG" `
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
shell: bash
run: ../repo/script/git-import-patches ../repo/patches
- name: Build (vcbuild.bat handles everything on Windows)
working-directory: node-src
shell: cmd
run: vcbuild.bat release x64 no-cctest package
- name: Smoke test
working-directory: node-src
shell: cmd
run: .\Release\node.exe --version
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: node-${{ env.NODE_TAG }}-win-x64
path: node-src/Release/node-${{ env.NODE_TAG }}-win-x64.zip
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# Windows ARM64
# ─────────────────────────────────────────────
build-windows-arm64:
if: inputs.windows-arm64
runs-on: windows-11-arm
steps:
- name: Install NASM (required by OpenSSL)
run: choco install nasm -y
- name: Clone Node.js ${{ env.NODE_TAG }}
run: |
git clone --depth 1 --branch "$env:NODE_TAG" `
https://github.com/nodejs/node.git node-src
- name: Checkout repository (for patches)
uses: actions/checkout@v6
with:
path: repo
- name: Apply patches
working-directory: node-src
shell: bash
run: ../repo/script/git-import-patches ../repo/patches
- name: Build
working-directory: node-src
shell: cmd
run: vcbuild.bat release arm64 no-cctest package
- name: Smoke test
working-directory: node-src
shell: cmd
run: .\Release\node.exe --version
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: node-${{ env.NODE_TAG }}-win-arm64
path: node-src/Release/node-${{ env.NODE_TAG }}-win-arm64.zip
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
# ─────────────────────────────────────────────
# Create GitHub Release and attach all binaries
# ─────────────────────────────────────────────
release:
needs:
- build-linux-x64
- build-linux-arm64
- build-macos-x64
- build-macos-arm64
- build-windows-x64
- build-windows-arm64
runs-on: ubuntu-latest
# Run as long as at least one build job succeeded; resolve-version alone is not enough
if: >-
always() && (
needs.build-linux-x64.result == 'success' ||
needs.build-linux-arm64.result == 'success' ||
needs.build-macos-x64.result == 'success' ||
needs.build-macos-arm64.result == 'success' ||
needs.build-windows-x64.result == 'success' ||
needs.build-windows-arm64.result == 'success'
)
permissions:
contents: write # required to create releases and upload assets
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v8
with:
path: artifacts # each artifact lands in artifacts/<artifact-name>/
- name: Flatten artifacts into release-assets/
run: |
mkdir release-assets
find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" \) \
-exec mv {} release-assets/ \;
ls -lh release-assets/
- name: Generate SHA256 checksums
working-directory: release-assets
if: >-
needs.build-linux-x64.result == 'success' &&
needs.build-linux-arm64.result == 'success' &&
needs.build-macos-x64.result == 'success' &&
needs.build-macos-arm64.result == 'success' &&
needs.build-windows-x64.result == 'success' &&
needs.build-windows-arm64.result == 'success'
run: |
sha256sum * > SHASUMS256.txt
cat SHASUMS256.txt
- name: Create GitHub Release and upload assets
run: |
if gh release view "$NODE_TAG" --repo "$GITHUB_REPOSITORY" &>/dev/null; then
echo "Release $NODE_TAG already exists — updating assets."
# Delete any existing assets that share a name with what we're about to upload
for file in release-assets/*; do
asset_name=$(basename "$file")
if gh release view "$NODE_TAG" --repo "$GITHUB_REPOSITORY" --json assets \
--jq ".assets[].name" | grep -qx "$asset_name"; then
echo "Deleting existing asset: $asset_name"
gh release delete-asset "$NODE_TAG" "$asset_name" \
--repo "$GITHUB_REPOSITORY" --yes
fi
done
# Upload the fresh assets
gh release upload "$NODE_TAG" release-assets/* \
--repo "$GITHUB_REPOSITORY"
else
echo "Creating new release $NODE_TAG."
gh release create "$NODE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "${NODE_TAG}" \
--notes "Custom build of Node.js ${NODE_TAG} from source that has pointer compression enabled. Verify downloads with \`SHASUMS256.txt\`." \
release-assets/*
fi