v0.5.8 #31
Workflow file for this run
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
| # Build Node.js SDK for all supported platforms. | |
| # | |
| # Self-contained workflow that builds and publishes Node.js packages to npm. | |
| # Uses napi-rs for native Rust bindings with platform-specific packages. | |
| # | |
| # Package structure: | |
| # - @boxlite-ai/boxlite (main package with TypeScript wrappers) | |
| # - @boxlite-ai/boxlite-darwin-arm64 (macOS ARM64 native binary) | |
| # - @boxlite-ai/boxlite-linux-x64-gnu (Linux x64 glibc native binary) | |
| # | |
| # Triggers: | |
| # - release: When a GitHub release is published | |
| # - workflow_dispatch: Manual trigger for testing | |
| name: Build Node.js | |
| on: | |
| release: | |
| types: [ published ] | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| config: | |
| uses: ./.github/workflows/config.yml | |
| # ============================================================================ | |
| # BUILD | |
| # ============================================================================ | |
| # Builds native Node.js bindings for each platform using napi-rs. | |
| # | |
| # Platform-specific considerations: | |
| # - Linux: Uses manylinux_2_28 container for glibc compatibility | |
| # - Guest binary built on host first (Ubuntu has musl-tools, manylinux doesn't) | |
| # - Shim/libkrun built in container (needs glibc 2.28) | |
| # - macOS: Builds directly on runner (ARM64 only) | |
| # | |
| # Outputs artifacts containing: | |
| # - native/boxlite.js (JS loader) | |
| # - npm/<platform>/boxlite.<platform>.node (native binary) | |
| # - npm/<platform>/runtime/* (boxlite-shim, boxlite-guest, libkrun, etc.) | |
| # ============================================================================ | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| needs: config | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJson(needs.config.outputs.platforms) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ needs.config.outputs.node-build-version }} | |
| - name: Set up Rust | |
| uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| with: | |
| toolchain: ${{ needs.config.outputs.rust-toolchain }} | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "boxlite" | |
| save-if: false | |
| # Build guest on host BEFORE manylinux container because: | |
| # - manylinux (AlmaLinux/RHEL) doesn't have musl packages in repos | |
| # - Building musl-cross-make from source takes ~15 minutes | |
| # - Ubuntu host has musl-tools via apt (~30 seconds) | |
| # - Guest is static musl binary, doesn't need manylinux glibc compatibility | |
| # - Shim/libkrun must be built IN container (needs glibc 2.28 for manylinux) | |
| - name: Build guest binary (Linux only) | |
| if: runner.os == 'Linux' | |
| run: | | |
| make setup guest | |
| # Cache only the final binary (11MB), not entire target dir (2.3GB) | |
| # Guest is not rebuilt in container (SKIP_GUEST_BUILD=1), so no incremental compilation needed | |
| GUEST_TARGET=$(scripts/util.sh --target) | |
| mkdir -p ".cache/$GUEST_TARGET/release" | |
| cp "target/$GUEST_TARGET/release/boxlite-guest" ".cache/$GUEST_TARGET/release/" | |
| # Clean up to save disk space | |
| rm -rf target ~/.rustup ~/.cargo | |
| - name: Build Node.js package (Linux) | |
| if: runner.os == 'Linux' | |
| uses: addnab/docker-run-action@v3 | |
| with: | |
| image: quay.io/pypa/manylinux_2_28_${{ contains(matrix.target, 'arm64') && 'aarch64' || 'x86_64' }} | |
| options: -v ${{ github.workspace }}:/work -w /work | |
| run: | | |
| set -ex | |
| # Mark directory as safe for git (ownership differs in container) | |
| git config --global --add safe.directory /work | |
| # Restore pre-built guest target directory | |
| GUEST_TARGET=$(scripts/util.sh --target) | |
| if [ -d ".cache/$GUEST_TARGET" ]; then | |
| echo "Restoring guest from .cache/$GUEST_TARGET" | |
| mkdir -p target | |
| cp -a ".cache/$GUEST_TARGET" "target/$GUEST_TARGET" | |
| fi | |
| # Build with SKIP_GUEST_BUILD=1 to use pre-built guest | |
| export SKIP_GUEST_BUILD=1 | |
| make setup runtime | |
| # Add cargo to PATH for npm commands (matches Makefile pattern) | |
| # (make installs cargo to ~/.cargo/bin but npm subprocesses don't inherit PATH) | |
| export PATH="$HOME/.cargo/bin:$PATH" | |
| # Build Node.js artifacts (no pack - CI uploads raw artifacts) | |
| cd sdks/node | |
| npm install --silent | |
| npm run build:native -- --release | |
| npm run artifacts | |
| npm run bundle:runtime | |
| - name: Build Node.js package (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| make setup runtime | |
| # Build Node.js artifacts (no pack - CI uploads raw artifacts) | |
| cd sdks/node | |
| npm install --silent | |
| npm run build:native -- --release | |
| npm run artifacts | |
| npm run bundle:runtime | |
| # Create tar archive to preserve Unix file permissions (especially execute bits). | |
| # GitHub Actions upload-artifact uses ZIP which doesn't preserve permissions. | |
| # See: https://github.com/actions/upload-artifact/issues/38 | |
| - name: Create artifact tarball | |
| run: tar -cvf sdks/node/artifacts-${{ matrix.target }}.tar -C sdks/node native/boxlite.js npm/ | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: artifacts-${{ matrix.target }} | |
| path: sdks/node/artifacts-${{ matrix.target }}.tar | |
| if-no-files-found: error | |
| retention-days: ${{ needs.config.outputs.artifact-retention-days }} | |
| # ============================================================================ | |
| # TEST | |
| # ============================================================================ | |
| # Tests the built packages on each platform with multiple Node.js versions. | |
| # | |
| # Key steps: | |
| # - Removes npm/* from git checkout (contains all platforms) | |
| # - Downloads only the matching platform's artifacts | |
| # - Verifies the package can be loaded and exports are accessible | |
| # ============================================================================ | |
| test: | |
| name: Test (${{ matrix.platform.target }} / Node ${{ matrix.node-version }}) | |
| needs: [ config, build ] | |
| runs-on: ${{ matrix.platform.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: ${{ fromJson(needs.config.outputs.platforms) }} | |
| node-version: ${{ fromJson(needs.config.outputs.node-versions) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| # Remove npm/* from git checkout to avoid platform mismatch | |
| # (git contains all platforms, but we only want the one from artifacts) | |
| - name: Clean npm workspaces | |
| run: rm -rf sdks/node/npm | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: artifacts-${{ matrix.platform.target }} | |
| path: sdks/node | |
| # Extract tar to restore file permissions (see build job comment) | |
| - name: Extract artifacts | |
| run: tar -xvf sdks/node/artifacts-${{ matrix.platform.target }}.tar -C sdks/node | |
| - name: Test package | |
| run: | | |
| cd sdks/node | |
| npm install --ignore-scripts | |
| npm run build | |
| node -e "import('./dist/index.js').then(m => console.log('BoxLite loaded:', Object.keys(m)))" | |
| # ============================================================================ | |
| # PUBLISH TO NPM | |
| # ============================================================================ | |
| # Publishes all packages to npm registry using OIDC Trusted Publishing. | |
| # | |
| # Key considerations: | |
| # - Runs on ubuntu-latest but publishes pre-built platform-specific binaries | |
| # - Install dependencies BEFORE downloading artifacts to avoid EBADPLATFORM | |
| # (if npm/* directories exist during install, npm validates platform restrictions | |
| # even with --workspaces=false, causing errors for darwin-arm64 on Linux) | |
| # - Must publish platform packages BEFORE main package (dependency order) | |
| # - Uses npm provenance for supply chain security | |
| # ============================================================================ | |
| publish: | |
| name: Publish to npm | |
| needs: [ config, build, test ] | |
| if: github.event_name == 'release' && github.event.action == 'published' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write # Required for npm OIDC Trusted Publishing | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| # Install BEFORE downloading artifacts to avoid EBADPLATFORM error. | |
| # If npm/* directories exist, npm validates platform restrictions even | |
| # with --workspaces=false, causing errors for darwin-arm64 on Linux. | |
| - name: Install dependencies | |
| run: cd sdks/node && npm install --ignore-scripts | |
| - name: Build TypeScript | |
| run: cd sdks/node && npm run build | |
| # Download pre-built artifacts from all platforms (darwin-arm64, linux-x64-gnu) | |
| # merge-multiple: true combines artifacts from different matrix jobs into one directory | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: sdks/node/artifacts | |
| pattern: artifacts-* | |
| merge-multiple: true | |
| # Extract all tar archives to restore file permissions | |
| - name: Extract artifacts | |
| run: | | |
| cd sdks/node | |
| for tarball in artifacts/*.tar; do | |
| echo "Extracting $tarball..." | |
| tar -xvf "$tarball" | |
| done | |
| - name: List artifacts | |
| run: | | |
| ls -la sdks/node/native/ | |
| ls -la sdks/node/npm/ | |
| ls -la sdks/node/npm/*/runtime/ || true | |
| # ------------------------------------------------------------------------- | |
| # NPM PUBLISH (Trusted Publishing via OIDC - no NPM_TOKEN needed) | |
| # ------------------------------------------------------------------------- | |
| # IMPORTANT: Publish order matters! | |
| # 1. Platform packages first: @boxlite-ai/boxlite-darwin-arm64, etc. | |
| # 2. Main package last: @boxlite-ai/boxlite | |
| # | |
| # The main package has optionalDependencies pointing to platform packages. | |
| # If we publish main first, npm install will fail because the platform | |
| # packages don't exist yet on the registry. | |
| # ------------------------------------------------------------------------- | |
| - name: Publish platform packages (workspaces) | |
| run: cd sdks/node && npm publish --provenance --access public --workspaces | |
| - name: Publish main package | |
| run: cd sdks/node && npm publish --provenance --access public | |
| # ============================================================================ | |
| # UPLOAD TO GITHUB RELEASE | |
| # ============================================================================ | |
| # Creates npm-compatible tarballs and uploads them to the GitHub Release. | |
| # | |
| # This provides an alternative installation method: | |
| # npm install https://github.com/boxlite-ai/boxlite/releases/download/v0.1.5/boxlite-ai-boxlite-0.1.5.tgz | |
| # | |
| # Uses `npm run pack:all` which: | |
| # - Creates properly versioned .tgz files (e.g., boxlite-ai-boxlite-0.1.5.tgz) | |
| # - Includes all platform packages and the main package | |
| # - Produces npm-installable tarballs (unlike raw tar.gz of directories) | |
| # ============================================================================ | |
| upload-to-release: | |
| name: Upload to GitHub Release | |
| needs: [ config, build, test ] | |
| if: github.event_name == 'release' && github.event.action == 'published' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # Required to upload assets to GitHub Release | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| # Install BEFORE downloading artifacts to avoid EBADPLATFORM error. | |
| # (same reason as publish job - npm validates platform restrictions if npm/* exists) | |
| - name: Install dependencies | |
| run: cd sdks/node && npm install --ignore-scripts | |
| # Download pre-built artifacts from all platforms (darwin-arm64, linux-x64-gnu) | |
| # merge-multiple: true combines artifacts from different matrix jobs into one directory | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: sdks/node/artifacts | |
| pattern: artifacts-* | |
| merge-multiple: true | |
| # Extract all tar archives to restore file permissions | |
| - name: Extract artifacts | |
| run: | | |
| cd sdks/node | |
| for tarball in artifacts/*.tar; do | |
| echo "Extracting $tarball..." | |
| tar -xvf "$tarball" | |
| done | |
| # pack:all creates versioned .tgz files for all packages: | |
| # - boxlite-ai-boxlite-X.Y.Z.tgz (main package) | |
| # - boxlite-ai-boxlite-darwin-arm64-X.Y.Z.tgz | |
| # - boxlite-ai-boxlite-linux-x64-gnu-X.Y.Z.tgz | |
| - name: Build and pack packages | |
| run: | | |
| cd sdks/node | |
| npm run build | |
| npm run pack:all | |
| ls -la packages/ | |
| - name: Upload to release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: sdks/node/packages/*.tgz | |
| fail_on_unmatched_files: true |