Update build.yml #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |