fix(ci): PR feedback #28
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
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| skip_publish: | |
| description: 'Skip npm publish (for testing)' | |
| type: boolean | |
| default: true | |
| jobs: | |
| build-and-test: | |
| name: Build & Test ${{ matrix.package }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # macOS | |
| - os: macos-14 | |
| arch: arm64 | |
| gpu: metal | |
| package: darwin-arm64 | |
| - os: macos-15-intel | |
| arch: x64 | |
| gpu: cpu | |
| package: darwin-x64 | |
| # Linux x64 | |
| - os: ubuntu-22.04 | |
| arch: x64 | |
| gpu: cpu | |
| package: linux-x64 | |
| - os: ubuntu-22.04 | |
| arch: x64 | |
| gpu: cuda | |
| package: linux-x64-cuda | |
| - os: ubuntu-22.04 | |
| arch: x64 | |
| gpu: vulkan | |
| package: linux-x64-vulkan | |
| # Linux ARM64 (native runners) | |
| - os: ubuntu-22.04-arm | |
| arch: arm64 | |
| gpu: cpu | |
| package: linux-arm64 | |
| - os: ubuntu-22.04-arm | |
| arch: arm64 | |
| gpu: cuda | |
| package: linux-arm64-cuda | |
| - os: ubuntu-22.04-arm | |
| arch: arm64 | |
| gpu: vulkan | |
| package: linux-arm64-vulkan | |
| # Windows x64 | |
| - os: windows-2022 | |
| arch: x64 | |
| gpu: cpu | |
| package: win32-x64 | |
| - os: windows-2022 | |
| arch: x64 | |
| gpu: cuda | |
| package: win32-x64-cuda | |
| - os: windows-2022 | |
| arch: x64 | |
| gpu: vulkan | |
| package: win32-x64-vulkan | |
| # Windows ARM64 (cross-compiled from x64) | |
| - os: windows-2022 | |
| arch: arm64 | |
| gpu: cpu | |
| package: win32-arm64 | |
| cross_compile: true | |
| - os: windows-2022 | |
| arch: arm64 | |
| gpu: vulkan | |
| package: win32-arm64-vulkan | |
| cross_compile: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Validate llama.cpp version | |
| run: node scripts/sync-llama-cpp.js --check | |
| shell: bash | |
| # Platform-specific build dependencies | |
| - name: Install build tools (Linux x64) | |
| if: runner.os == 'Linux' && matrix.arch == 'x64' && matrix.gpu == 'cpu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential cmake | |
| - name: Install build tools (Linux ARM64) | |
| if: runner.os == 'Linux' && matrix.arch == 'arm64' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential cmake | |
| - name: Provision CUDA toolkit (Linux) | |
| if: matrix.gpu == 'cuda' && runner.os == 'Linux' | |
| uses: ./.github/actions/provision-cuda | |
| with: | |
| version: '12.6.2' | |
| arch: ${{ matrix.arch }} | |
| - name: Install Vulkan SDK (Linux) | |
| if: matrix.gpu == 'vulkan' && runner.os == 'Linux' | |
| uses: jakoch/install-vulkan-sdk-action@v1.2.4 | |
| with: | |
| vulkan_version: '1.4.313.0' | |
| install_runtime: true | |
| optional_components: com.lunarg.vulkan.arm64 | |
| cache: true | |
| stripdown: true | |
| - name: Provision CUDA toolkit (Windows) | |
| if: matrix.gpu == 'cuda' && runner.os == 'Windows' | |
| uses: ./.github/actions/provision-cuda | |
| with: | |
| version: '12.6.2' | |
| arch: ${{ matrix.arch }} | |
| - name: Install Vulkan SDK (Windows) | |
| if: matrix.gpu == 'vulkan' && runner.os == 'Windows' | |
| uses: jakoch/install-vulkan-sdk-action@v1.2.4 | |
| with: | |
| vulkan_version: '1.4.313.0' | |
| install_runtime: true | |
| cache: true | |
| # Note: Do NOT use stripdown on Windows - it removes vulkan-1.dll runtime | |
| - name: Setup LLVM and Ninja for Windows ARM64 cross-compilation | |
| if: runner.os == 'Windows' && matrix.cross_compile == true | |
| shell: pwsh | |
| run: | | |
| choco install llvm ninja -y | |
| echo "CC=clang-cl" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| echo "CXX=clang-cl" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| echo "CMAKE_GENERATOR=Ninja" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| # Build | |
| - name: Install npm dependencies | |
| run: npm install --ignore-scripts | |
| - name: Build native module (Native builds) | |
| if: matrix.cross_compile != true | |
| run: npm run build | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| - name: Build native module (Windows ARM64 cross-compile) | |
| if: runner.os == 'Windows' && matrix.cross_compile == true | |
| shell: pwsh | |
| run: | | |
| $env:CMAKE_GENERATOR = "Ninja" | |
| $env:CMAKE_TOOLCHAIN_FILE = "${{ github.workspace }}/cmake/arm64-cross.cmake" | |
| npm run build | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| ARCH: arm64 | |
| # Create platform package | |
| - name: Create platform package | |
| shell: bash | |
| run: | | |
| node scripts/create-platform-package.js "${{ matrix.package }}" "${{ matrix.os }}" "${{ matrix.arch }}" | |
| # Test platform package (skip for cross-compiled packages - can't run ARM64 on x64) | |
| - name: Cache test models | |
| if: matrix.cross_compile != true | |
| uses: actions/cache@v4 | |
| with: | |
| path: models/ | |
| key: test-models-v1 | |
| - name: Download test models | |
| if: matrix.cross_compile != true | |
| run: bash scripts/download-test-models.sh | |
| - name: Install platform package locally | |
| if: matrix.cross_compile != true | |
| shell: bash | |
| run: | | |
| npm install ./packages/${{ matrix.package }} | |
| rm -rf build/Release | |
| echo "Installed package contents:" | |
| ls -la node_modules/@lloyal-labs/lloyal.node-${{ matrix.package }}/bin/ || echo "Package not found in node_modules" | |
| - name: Verify platform package loading (Unix) | |
| if: runner.os != 'Windows' && matrix.cross_compile != true && matrix.gpu != 'cuda' | |
| shell: bash | |
| run: | | |
| # Set LD_LIBRARY_PATH to find sibling .so files (Linux CUDA/Vulkan) | |
| PKG_BIN="$PWD/node_modules/@lloyal-labs/lloyal.node-${{ matrix.package }}/bin" | |
| export LD_LIBRARY_PATH="${PKG_BIN}:${LD_LIBRARY_PATH:-}" | |
| echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH" | |
| node -e " | |
| const { loadBinary } = require('./lib'); | |
| const addon = loadBinary(); | |
| console.log('✓ Platform package loaded successfully'); | |
| console.log(' Exports:', Object.keys(addon)); | |
| " | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| LLOYAL_NO_FALLBACK: '1' | |
| - name: Verify platform package loading (Windows) | |
| if: runner.os == 'Windows' && matrix.cross_compile != true && matrix.gpu != 'cuda' | |
| shell: pwsh | |
| run: | | |
| # Add package bin to PATH for DLL discovery | |
| $pkgBin = "$PWD\node_modules\@lloyal-labs\lloyal.node-${{ matrix.package }}\bin" | |
| $env:PATH = "$pkgBin;$env:PATH" | |
| # Add Vulkan SDK runtime to PATH if available | |
| if ($env:VULKAN_SDK) { | |
| $env:PATH = "$env:VULKAN_SDK\Bin;$env:PATH" | |
| # Vulkan runtime is in runtime\x64, not Bin | |
| if (Test-Path "$env:VULKAN_SDK\runtime\x64\vulkan-1.dll") { | |
| $env:PATH = "$env:VULKAN_SDK\runtime\x64;$env:PATH" | |
| Write-Host "Added Vulkan runtime to PATH: $env:VULKAN_SDK\runtime\x64" | |
| } | |
| } | |
| # Add CUDA runtime to PATH if available | |
| if ($env:CUDA_PATH) { | |
| $env:PATH = "$env:CUDA_PATH\bin;$env:PATH" | |
| Write-Host "Added CUDA bin to PATH: $env:CUDA_PATH\bin" | |
| } | |
| Write-Host "Package bin: $pkgBin" | |
| Write-Host "VULKAN_SDK: $env:VULKAN_SDK" | |
| Write-Host "CUDA_PATH: $env:CUDA_PATH" | |
| node -e " | |
| const { loadBinary } = require('./lib'); | |
| const addon = loadBinary(); | |
| console.log('✓ Platform package loaded successfully'); | |
| console.log(' Exports:', Object.keys(addon)); | |
| " | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| LLOYAL_NO_FALLBACK: '1' | |
| # Integration tests only run where we have real GPU hardware | |
| # Currently: CPU (all platforms) and Metal (macOS) | |
| # TODO: Re-enable vulkan/cuda when self-hosted GPU runners are available | |
| - name: Run integration tests (Windows) | |
| if: runner.os == 'Windows' && matrix.gpu == 'cpu' && matrix.cross_compile != true | |
| shell: pwsh | |
| run: | | |
| $pkgBin = "$PWD\node_modules\@lloyal-labs\lloyal.node-${{ matrix.package }}\bin" | |
| $env:PATH = "$pkgBin;$env:PATH" | |
| if ($env:VULKAN_SDK) { | |
| $env:PATH = "$env:VULKAN_SDK\Bin;$env:PATH" | |
| if (Test-Path "$env:VULKAN_SDK\runtime\x64\vulkan-1.dll") { | |
| $env:PATH = "$env:VULKAN_SDK\runtime\x64;$env:PATH" | |
| } | |
| } | |
| if ($env:CUDA_PATH) { $env:PATH = "$env:CUDA_PATH\bin;$env:PATH" } | |
| npm run test:integration | |
| timeout-minutes: 5 | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| LLOYAL_NO_FALLBACK: '1' | |
| - name: Run integration tests (Unix) | |
| if: runner.os != 'Windows' && (matrix.gpu == 'cpu' || matrix.gpu == 'metal') | |
| run: | | |
| PKG_BIN="$PWD/node_modules/@lloyal-labs/lloyal.node-${{ matrix.package }}/bin" | |
| export LD_LIBRARY_PATH="${PKG_BIN}:${LD_LIBRARY_PATH:-}" | |
| npm run test:integration | |
| timeout-minutes: 5 | |
| env: | |
| LLOYAL_GPU: ${{ matrix.gpu }} | |
| LLOYAL_NO_FALLBACK: '1' | |
| # Upload package artifact for publish job | |
| - name: Upload platform package artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: package-${{ matrix.package }} | |
| path: packages/${{ matrix.package }}/ | |
| retention-days: 1 | |
| # GPU Integration Tests via Cloud Run | |
| # Runs real GPU tests on NVIDIA L4 for CUDA/Vulkan packages | |
| gpu-integration: | |
| name: GPU Tests (${{ matrix.backend }}) | |
| needs: build-and-test | |
| runs-on: ubuntu-latest | |
| if: success() | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Workload Identity Federation | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - backend: cuda | |
| package: linux-x64-cuda | |
| - backend: vulkan | |
| package: linux-x64-vulkan | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} | |
| service_account: ${{ secrets.GCP_SA_EMAIL }} | |
| - name: Set up Cloud SDK | |
| uses: google-github-actions/setup-gcloud@v2 | |
| - name: Configure Docker for Artifact Registry | |
| run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet | |
| # Download built packages from build-and-test job | |
| - name: Download package artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: package-${{ matrix.package }} | |
| path: packages/package-${{ matrix.package }} | |
| # Build and push Docker image | |
| - name: Build GPU test image | |
| run: | | |
| IMAGE="us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/lloyal-ci/gpu-tests:${{ github.sha }}-${{ matrix.backend }}" | |
| docker build \ | |
| -f ci/Dockerfile.gpu-tests \ | |
| -t "$IMAGE" . | |
| docker push "$IMAGE" | |
| echo "IMAGE=$IMAGE" >> $GITHUB_ENV | |
| # Create/update Cloud Run Job | |
| - name: Deploy Cloud Run Job | |
| run: | | |
| JOB_NAME="lloyal-gpu-test-${{ matrix.backend }}" | |
| # Check if job exists | |
| if gcloud run jobs describe $JOB_NAME --region=us-central1 2>/dev/null; then | |
| gcloud run jobs update $JOB_NAME \ | |
| --region=us-central1 \ | |
| --image="${IMAGE}" \ | |
| --service-account="${{ secrets.GCP_SA_EMAIL }}" \ | |
| --set-env-vars=LLOYAL_GPU=${{ matrix.backend }},LLOYAL_NO_FALLBACK=1 \ | |
| --task-timeout=10m | |
| else | |
| gcloud run jobs create $JOB_NAME \ | |
| --region=us-central1 \ | |
| --image="${IMAGE}" \ | |
| --service-account="${{ secrets.GCP_SA_EMAIL }}" \ | |
| --set-env-vars=LLOYAL_GPU=${{ matrix.backend }},LLOYAL_NO_FALLBACK=1 \ | |
| --task-timeout=10m \ | |
| --gpu=1 \ | |
| --gpu-type=nvidia-l4 \ | |
| --memory=8Gi \ | |
| --cpu=2 \ | |
| --max-retries=0 | |
| fi | |
| # Execute the job and wait for completion | |
| - name: Run GPU tests | |
| run: | | |
| JOB_NAME="lloyal-gpu-test-${{ matrix.backend }}" | |
| # Execute job | |
| EXECUTION=$(gcloud run jobs execute $JOB_NAME \ | |
| --region=us-central1 \ | |
| --wait \ | |
| --format='value(metadata.name)') | |
| echo "Execution: $EXECUTION" | |
| # Wait for logs to flush to Cloud Logging | |
| sleep 5 | |
| # Get logs | |
| gcloud logging read \ | |
| "resource.type=cloud_run_job AND resource.labels.job_name=$JOB_NAME AND resource.labels.location=us-central1" \ | |
| --limit=200 \ | |
| --format='value(textPayload)' | |
| publish: | |
| name: Publish all packages | |
| needs: [build-and-test, gpu-integration] | |
| runs-on: ubuntu-latest | |
| # Only run if ALL jobs succeeded AND not a test tag AND not manual skip | |
| # Use v0.0.0-gpu-test or workflow_dispatch with skip_publish to test CI | |
| if: success() && !contains(github.ref_name, '-test') && github.event.inputs.skip_publish != true | |
| permissions: | |
| contents: read | |
| id-token: write # Required for npm provenance | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| registry-url: 'https://registry.npmjs.org' | |
| # Download all platform package artifacts | |
| - name: Download all platform packages | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: packages/ | |
| pattern: package-* | |
| merge-multiple: false | |
| - name: List downloaded packages | |
| run: | | |
| echo "Downloaded platform packages:" | |
| ls -la packages/ | |
| for dir in packages/package-*/; do | |
| echo " - $(basename $dir): $(ls $dir)" | |
| done | |
| # Publish platform packages | |
| - name: Publish platform packages | |
| shell: bash | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| echo "Publishing version: $VERSION" | |
| # Determine npm tag based on version | |
| if [[ "$VERSION" == *-alpha* ]]; then | |
| NPM_TAG="alpha" | |
| elif [[ "$VERSION" == *-beta* ]]; then | |
| NPM_TAG="beta" | |
| elif [[ "$VERSION" == *-rc* ]]; then | |
| NPM_TAG="rc" | |
| else | |
| NPM_TAG="latest" | |
| fi | |
| echo "Using npm tag: $NPM_TAG" | |
| # Publish each platform package | |
| for pkg_dir in packages/package-*/; do | |
| pkg_name=$(basename "$pkg_dir" | sed 's/^package-//') | |
| echo "" | |
| echo "Publishing @lloyal-labs/lloyal.node-${pkg_name}..." | |
| cd "$pkg_dir" | |
| npm publish --access public --tag "$NPM_TAG" --provenance | |
| cd - > /dev/null | |
| done | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # Publish main package | |
| - name: Sync package versions | |
| run: node scripts/sync-versions.js | |
| - name: Publish main package | |
| shell: bash | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| if [[ "$VERSION" == *-alpha* ]]; then | |
| npm publish --tag alpha --access public --provenance | |
| elif [[ "$VERSION" == *-beta* ]]; then | |
| npm publish --tag beta --access public --provenance | |
| elif [[ "$VERSION" == *-rc* ]]; then | |
| npm publish --tag rc --access public --provenance | |
| else | |
| npm publish --access public --provenance | |
| fi | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} |