Skip to content

Update build.yml

Update build.yml #30

Workflow file for this run

name: Build libnode
on:
push:
branches: [main]
workflow_dispatch:
inputs:
node_version:
description: 'Node.js version (e.g., 22.13.0). Leave empty for latest LTS.'
required: false
default: ''
force_rebuild:
description: 'Force rebuild even if release exists'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
include_x86:
description: 'Include Windows x86 build (not supported on Node >= 25)'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_LTS_MAJOR: 22
jobs:
resolve-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
skip_build: ${{ steps.decide.outputs.skip_build }}
steps:
- name: Resolve Node.js version
id: resolve
run: |
if [ -n "${{ github.event.inputs.node_version }}" ]; then
VERSION="${{ github.event.inputs.node_version }}"
else
VERSION=$(curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.lts != false) | select(.version | startswith("v${{ env.NODE_LTS_MAJOR }}"))][0].version' | sed 's/^v//')
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Resolved Node.js version: $VERSION"
- name: Check if release already exists
id: check_release
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ steps.resolve.outputs.version }}"
TAG="libnode-v${VERSION}"
if gh release view "$TAG" --repo ${{ github.repository }} > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "::notice::Release $TAG already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "::notice::Release $TAG does not exist, will build"
fi
- name: Decide what to build
id: decide
run: |
FORCE="${{ github.event.inputs.force_rebuild }}"
EXISTS="${{ steps.check_release.outputs.exists }}"
if [ "$EXISTS" == "true" ] && [ "$FORCE" != "true" ]; then
echo "skip_build=true" >> $GITHUB_OUTPUT
echo "::warning::Skipping build - release already exists"
else
echo "skip_build=false" >> $GITHUB_OUTPUT
fi
build-windows:
needs: resolve-version
if: needs.resolve-version.outputs.skip_build != 'true'
runs-on: windows-latest
strategy:
matrix:
arch:
- x64
include: ${{ github.event.inputs.include_x86 == 'true' && fromJson('[{"arch":"x86"}]') || fromJson('[]') }}
steps:
- name: Free disk space
shell: pwsh
run: |
$dirs = @(
"C:\hostedtoolcache\CodeQL",
"C:\hostedtoolcache\Java_Temurin-Hotspot_jdk",
"C:\hostedtoolcache\Ruby",
"C:\hostedtoolcache\go",
"C:\mingw64",
"C:\Program Files\dotnet\sdk",
"C:\Program Files\dotnet\shared"
)
foreach ($d in $dirs) {
if (Test-Path $d) { Remove-Item -Recurse -Force $d -ErrorAction SilentlyContinue }
}
Get-PSDrive C | Select-Object Used,Free
- name: Install NASM
shell: pwsh
run: |
choco install nasm --no-progress -y --timeout 300
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
- name: Clone Node.js
run: |
git clone --depth 1 --branch v${{ needs.resolve-version.outputs.version }} https://github.com/nodejs/node.git node-src
- name: Build Node.js (shared library)
shell: cmd
run: |
cd node-src
set NUMBER_OF_PROCESSORS=2
call vcbuild.bat release dll ${{ matrix.arch }} full-icu
- name: Prepare artifacts
shell: pwsh
run: |
$version = "${{ needs.resolve-version.outputs.version }}"
$arch = "${{ matrix.arch }}"
$outDir = "libnode-v$version-win-$arch"
New-Item -ItemType Directory -Force -Path "$outDir/lib"
New-Item -ItemType Directory -Force -Path "$outDir/include"
New-Item -ItemType Directory -Force -Path "$outDir/bin"
# Copy libraries
Copy-Item "node-src/out/Release/libnode.dll" "$outDir/lib/"
Copy-Item "node-src/out/Release/libnode.lib" "$outDir/lib/"
Copy-Item "node-src/out/Release/lib/libuv.lib" "$outDir/lib/"
Copy-Item "node-src/out/Release/lib/v8_libbase.lib" "$outDir/lib/"
Copy-Item "node-src/out/Release/lib/v8_libplatform.lib" "$outDir/lib/"
# Copy abseil library (required by v8_libbase in V8 13+/Node.js 25+)
if (Test-Path "node-src/out/Release/lib/abseil.lib") {
Copy-Item "node-src/out/Release/lib/abseil.lib" "$outDir/lib/"
}
# Copy headers (top-level + all subdirectories containing .h files)
Copy-Item -Recurse "node-src/src/*.h" "$outDir/include/"
Get-ChildItem -Path "node-src/src" -Directory | ForEach-Object {
$hFiles = Get-ChildItem -Path $_.FullName -Filter "*.h" -ErrorAction SilentlyContinue
if ($hFiles) {
$subdir = $_.Name
New-Item -ItemType Directory -Force -Path "$outDir/include/$subdir" | Out-Null
Copy-Item "$($_.FullName)/*.h" "$outDir/include/$subdir/"
}
}
Copy-Item -Recurse "node-src/deps/v8/include/*" "$outDir/include/"
Copy-Item -Recurse "node-src/deps/uv/include/*" "$outDir/include/"
# Copy node executable
Copy-Item "node-src/out/Release/node.exe" "$outDir/bin/"
# Create zip
Compress-Archive -Path "$outDir" -DestinationPath "libnode-v$version-win-$arch.zip"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: libnode-win-${{ matrix.arch }}
path: libnode-v${{ needs.resolve-version.outputs.version }}-win-${{ matrix.arch }}.zip
build-macos:
needs: resolve-version
if: needs.resolve-version.outputs.skip_build != 'true'
runs-on: macos-15
steps:
- name: Clone Node.js
run: |
git clone --depth 1 --branch v${{ needs.resolve-version.outputs.version }} https://github.com/nodejs/node.git node-src
- name: Build Node.js (shared library)
run: |
cd node-src
./configure --shared --with-intl=full-icu
make -j$(sysctl -n hw.ncpu)
- name: Prepare artifacts
run: |
VERSION="${{ needs.resolve-version.outputs.version }}"
OUTDIR="libnode-v$VERSION-darwin-arm64"
mkdir -p "$OUTDIR/lib"
mkdir -p "$OUTDIR/include"
mkdir -p "$OUTDIR/bin"
# Copy libraries (dylib is versioned, e.g., libnode.127.dylib)
cp node-src/out/Release/libnode*.dylib "$OUTDIR/lib/"
# Create unversioned symlink for convenience
cd "$OUTDIR/lib" && ln -sf libnode.*.dylib libnode.dylib && cd -
# Copy headers (top-level + all subdirectories containing .h files)
cp node-src/src/*.h "$OUTDIR/include/"
find node-src/src -mindepth 1 -maxdepth 1 -type d | while IFS= read -r dir; do
if ls "$dir"/*.h &>/dev/null; then
subdir=$(basename "$dir")
mkdir -p "$OUTDIR/include/$subdir"
cp "$dir"/*.h "$OUTDIR/include/$subdir/"
fi
done
cp -R node-src/deps/v8/include/* "$OUTDIR/include/"
cp -R node-src/deps/uv/include/* "$OUTDIR/include/"
# Copy node executable
cp node-src/out/Release/node "$OUTDIR/bin/"
# Create tarball
tar -czvf "libnode-v$VERSION-darwin-arm64.tar.gz" "$OUTDIR"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: libnode-darwin-arm64
path: libnode-v${{ needs.resolve-version.outputs.version }}-darwin-arm64.tar.gz
build-linux:
needs: resolve-version
if: needs.resolve-version.outputs.skip_build != 'true'
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential python3
- name: Clone Node.js
run: |
git clone --depth 1 --branch v${{ needs.resolve-version.outputs.version }} https://github.com/nodejs/node.git node-src
- name: Build Node.js (shared library)
run: |
cd node-src
./configure --shared --with-intl=full-icu
make -j$(nproc)
- name: Prepare artifacts
run: |
VERSION="${{ needs.resolve-version.outputs.version }}"
OUTDIR="libnode-v$VERSION-linux-x64"
mkdir -p "$OUTDIR/lib"
mkdir -p "$OUTDIR/include"
mkdir -p "$OUTDIR/bin"
# Copy shared library
cp node-src/out/Release/lib.target/libnode.so* "$OUTDIR/lib/" || cp node-src/out/Release/libnode.so* "$OUTDIR/lib/"
# Copy static libraries
find node-src/out/Release -name "libuv.a" -exec cp {} "$OUTDIR/lib/" \;
find node-src/out/Release -name "v8_libbase.a" -exec cp {} "$OUTDIR/lib/" \;
find node-src/out/Release -name "v8_libplatform.a" -exec cp {} "$OUTDIR/lib/" \;
# Copy abseil library (required by v8_libbase in V8 13+/Node.js 25+)
find node-src/out/Release -name "libabseil.a" -exec cp {} "$OUTDIR/lib/" \; 2>/dev/null || true
# Copy headers (top-level + all subdirectories containing .h files)
cp node-src/src/*.h "$OUTDIR/include/"
find node-src/src -mindepth 1 -maxdepth 1 -type d | while IFS= read -r dir; do
if ls "$dir"/*.h &>/dev/null; then
subdir=$(basename "$dir")
mkdir -p "$OUTDIR/include/$subdir"
cp "$dir"/*.h "$OUTDIR/include/$subdir/"
fi
done
cp -R node-src/deps/v8/include/* "$OUTDIR/include/"
cp -R node-src/deps/uv/include/* "$OUTDIR/include/"
# Copy node executable
cp node-src/out/Release/node "$OUTDIR/bin/"
# Create tarball
tar -czvf "libnode-v$VERSION-linux-x64.tar.gz" "$OUTDIR"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: libnode-linux-x64
path: libnode-v${{ needs.resolve-version.outputs.version }}-linux-x64.tar.gz
release:
needs: [resolve-version, build-windows, build-macos, build-linux]
if: |
always() &&
needs.resolve-version.outputs.skip_build != 'true' &&
needs.build-windows.result == 'success' &&
needs.build-macos.result == 'success' &&
needs.build-linux.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: libnode-*
path: artifacts
- name: Create Release
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ needs.resolve-version.outputs.version }}"
TAG="libnode-v${VERSION}"
FILES=$(find artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \))
# Delete existing release if present (force rebuild case)
gh release delete "$TAG" --repo ${{ github.repository }} --yes 2>/dev/null || true
git push --delete origin "$TAG" 2>/dev/null || true
gh release create "$TAG" \
--repo ${{ github.repository }} \
--title "libnode v${VERSION}" \
--notes "Pre-built Node.js ${VERSION} libraries for embedding in C++ projects.
## Contents
### Windows (x64)
- libnode.dll / libnode.lib
- libuv.lib
- v8_libbase.lib, v8_libplatform.lib, abseil.lib
### macOS (arm64)
- libnode.*.dylib (versioned)
- libnode.dylib (symlink)
### Linux (x64)
- libnode.so
- libuv.a
- v8_libbase.a, v8_libplatform.a, libabseil.a
All archives include Node.js, V8, and libuv headers.
## Usage
Link against libnode and include the headers for full Node.js runtime embedding (require(), npm modules, async I/O, etc.)." \
$FILES