From 75db9796f88307cffe03085a62e8a139e4c2d28d Mon Sep 17 00:00:00 2001 From: rUv Date: Thu, 27 Nov 2025 03:50:31 +0000 Subject: [PATCH] ci: Add GNN multi-platform build workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds @ruvector/gnn native bindings for: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc Supports manual publish trigger and tag-based releases. 🤖 Generated with Claude Code --- .github/workflows/build-gnn.yml | 214 ++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 .github/workflows/build-gnn.yml diff --git a/.github/workflows/build-gnn.yml b/.github/workflows/build-gnn.yml new file mode 100644 index 00000000..69f74f6e --- /dev/null +++ b/.github/workflows/build-gnn.yml @@ -0,0 +1,214 @@ +name: Build GNN Native Modules + +on: + push: + branches: [main] + paths: + - 'crates/ruvector-gnn/**' + - 'crates/ruvector-gnn-node/**' + - '.github/workflows/build-gnn.yml' + tags: + - 'v*' + pull_request: + branches: [main] + paths: + - 'crates/ruvector-gnn/**' + - 'crates/ruvector-gnn-node/**' + workflow_dispatch: + inputs: + publish: + description: 'Publish to npm after build' + required: false + type: boolean + default: false + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + platform: linux-x64-gnu + - host: ubuntu-22.04 + target: aarch64-unknown-linux-gnu + platform: linux-arm64-gnu + - host: macos-13 + target: x86_64-apple-darwin + platform: darwin-x64 + - host: macos-14 + target: aarch64-apple-darwin + platform: darwin-arm64 + - host: windows-2022 + target: x86_64-pc-windows-msvc + platform: win32-x64-msvc + + name: Build GNN ${{ matrix.settings.platform }} + runs-on: ${{ matrix.settings.host }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + key: gnn-${{ matrix.settings.target }} + + - name: Install cross-compilation tools (Linux ARM64) + if: matrix.settings.platform == 'linux-arm64-gnu' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + + - name: Install dependencies + working-directory: crates/ruvector-gnn-node + run: npm install + + - name: Build native module + working-directory: crates/ruvector-gnn-node + run: | + npx napi build --platform --release --target ${{ matrix.settings.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + + - name: Find built .node files (debug) + shell: bash + run: | + echo "=== Searching for GNN .node files ===" + find crates/ruvector-gnn-node -name "*.node" -type f 2>/dev/null || true + + - name: Prepare artifact + shell: bash + run: | + mkdir -p gnn-artifacts/${{ matrix.settings.platform }} + + # Find the built .node file + NODE_FILE=$(find crates/ruvector-gnn-node -name "ruvector-gnn.*.node" -type f | head -1) + + if [ -z "$NODE_FILE" ]; then + echo "ERROR: No .node file found" + find crates/ruvector-gnn-node -name "*.node" -type f + exit 1 + fi + + echo "Found: $NODE_FILE" + cp -v "$NODE_FILE" "gnn-artifacts/${{ matrix.settings.platform }}/" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gnn-bindings-${{ matrix.settings.platform }} + path: gnn-artifacts/${{ matrix.settings.platform }}/*.node + if-no-files-found: error + + publish: + name: Publish GNN Platform Packages + runs-on: ubuntu-22.04 + needs: build + if: | + inputs.publish == true || + startsWith(github.ref, 'refs/tags/v') + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + registry-url: 'https://registry.npmjs.org' + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create and publish platform packages + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + VERSION=$(node -p "require('./crates/ruvector-gnn-node/package.json').version") + echo "Publishing version: $VERSION" + + for dir in artifacts/gnn-bindings-*/; do + platform=$(basename "$dir" | sed 's/gnn-bindings-//') + NODE_FILE=$(find "$dir" -name "*.node" | head -1) + + if [ -z "$NODE_FILE" ]; then + echo "No .node file found in $dir" + continue + fi + + echo "=== Publishing @ruvector/gnn-${platform}@${VERSION} ===" + + # Create package directory + PKG_DIR="npm-pkg/gnn-${platform}" + mkdir -p "$PKG_DIR" + + # Determine OS and CPU + case "$platform" in + linux-x64-gnu) + OS="linux"; CPU="x64"; LIBC='"libc": ["glibc"],' + NODE_NAME="ruvector-gnn.linux-x64-gnu.node" + ;; + linux-arm64-gnu) + OS="linux"; CPU="arm64"; LIBC='"libc": ["glibc"],' + NODE_NAME="ruvector-gnn.linux-arm64-gnu.node" + ;; + darwin-x64) + OS="darwin"; CPU="x64"; LIBC="" + NODE_NAME="ruvector-gnn.darwin-x64.node" + ;; + darwin-arm64) + OS="darwin"; CPU="arm64"; LIBC="" + NODE_NAME="ruvector-gnn.darwin-arm64.node" + ;; + win32-x64-msvc) + OS="win32"; CPU="x64"; LIBC="" + NODE_NAME="ruvector-gnn.win32-x64-msvc.node" + ;; + esac + + # Copy and rename binary + cp "$NODE_FILE" "$PKG_DIR/$NODE_NAME" + + # Create package.json + cat > "$PKG_DIR/package.json" << EOF + { + "name": "@ruvector/gnn-${platform}", + "version": "${VERSION}", + "os": ["${OS}"], + "cpu": ["${CPU}"], + ${LIBC} + "main": "${NODE_NAME}", + "files": ["${NODE_NAME}"], + "description": "Graph Neural Network capabilities for Ruvector - ${platform} platform", + "keywords": ["ruvector", "gnn", "graph-neural-network", "napi-rs"], + "author": "Ruvector Team", + "license": "MIT", + "repository": {"type": "git", "url": "https://github.com/ruvnet/ruvector"}, + "engines": {"node": ">= 10"}, + "publishConfig": {"registry": "https://registry.npmjs.org/", "access": "public"} + } + EOF + + # Publish + cd "$PKG_DIR" + npm publish --access public || echo "Failed to publish @ruvector/gnn-${platform}" + cd ../.. + done