triviumdb 0.7.0

A high-performance memory-mmap hybrid search engine built for AI, combining dense vector, sparse text, graph relations, and JSON metadata.
Documentation
name: Publish to PyPI & NPM

# 触发条件:当推送形如 vX.Y.Z 的 tag 时触发
on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+*'

jobs:
  # ═══════════════════════════════════════════════════════════
  #  阶段 0: 版本预检(PyPI / NPM 是否已存在相同或更新版本)
  # ═══════════════════════════════════════════════════════════
  version-gate:
    name: Version Gate
    runs-on: ubuntu-latest
    outputs:
      pypi_should_publish: ${{ steps.check_pypi.outputs.should_publish }}
      npm_should_publish: ${{ steps.check_npm.outputs.should_publish }}
      version: ${{ steps.extract.outputs.version }}
    steps:
      - uses: actions/checkout@v4

      - name: 提取版本号
        id: extract
        run: |

          # 从 tag 名提取版本:v0.6.0 → 0.6.0
          VERSION="${GITHUB_REF_NAME#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          echo "📦 当前发布版本: $VERSION"

      - name: 检查 PyPI 是否已发布
        id: check_pypi
        run: |

          VERSION="${{ steps.extract.outputs.version }}"
          HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/triviumdb/${VERSION}/json")
          if [ "$HTTP_CODE" = "200" ]; then
            echo "⏭️  PyPI 已存在 triviumdb==${VERSION},跳过发布"
            echo "should_publish=false" >> "$GITHUB_OUTPUT"
          else
            echo "✅ PyPI 未找到 triviumdb==${VERSION},准备发布"
            echo "should_publish=true" >> "$GITHUB_OUTPUT"
          fi

      - name: 检查 NPM 是否已发布
        id: check_npm
        run: |

          VERSION="${{ steps.extract.outputs.version }}"
          # npm view 返回版本号或空
          EXISTING=$(npm view triviumdb@${VERSION} version 2>/dev/null || echo "")
          if [ "$EXISTING" = "$VERSION" ]; then
            echo "⏭️  NPM 已存在 triviumdb@${VERSION},跳过发布"
            echo "should_publish=false" >> "$GITHUB_OUTPUT"
          else
            echo "✅ NPM 未找到 triviumdb@${VERSION},准备发布"
            echo "should_publish=true" >> "$GITHUB_OUTPUT"
          fi

  # ═══════════════════════════════════════════════════════════
  #  阶段 1: 编译 Python Wheels(含 ARM64)
  # ═══════════════════════════════════════════════════════════
  build-wheels:
    name: Build wheels (${{ matrix.name }})
    runs-on: ${{ matrix.runner }}
    needs: version-gate
    if: needs.version-gate.outputs.pypi_should_publish == 'true'
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Linux x86_64
            runner: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            sdist: true
          - name: Linux ARM64
            runner: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            sdist: false
          - name: Windows x86_64
            runner: windows-latest
            target: x86_64-pc-windows-msvc
            sdist: false
          - name: macOS x86_64
            runner: macos-latest  # Apple Silicon 交叉编译 x86_64
            target: x86_64-apple-darwin
            sdist: false
          - name: macOS ARM64
            runner: macos-latest
            target: aarch64-apple-darwin
            sdist: false
    steps:
      - uses: actions/checkout@v4

      - name: Setup Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'

      - name: Build wheels
        uses: PyO3/maturin-action@v1
        env:
          PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
        with:
          command: build
          target: ${{ matrix.target }}
          args: --release --features python -o dist ${{ matrix.sdist && '--sdist' || '' }}
          rust-toolchain: stable
          # Linux ARM64 使用 manylinux 2_28 + Zig 交叉编译,避免 Docker 镜像解压失败
          manylinux: ${{ contains(matrix.target, 'aarch64') && '2_28' || 'auto' }}
          docker-options: ${{ contains(matrix.target, 'aarch64-unknown-linux') && '-e JEMALLOC_SYS_WITH_LG_PAGE=16' || '' }}

      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.name }}
          path: dist

  # ═══════════════════════════════════════════════════════════
  #  阶段 2: 发布到 PyPI
  # ═══════════════════════════════════════════════════════════
  release:
    name: Release to PyPI
    runs-on: ubuntu-latest
    needs: [version-gate, build-wheels]
    if: needs.version-gate.outputs.pypi_should_publish == 'true'
    steps:
      - name: Download all wheels
        uses: actions/download-artifact@v4
        with:
          path: dist
          pattern: wheels-*
          merge-multiple: true

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_API_TOKEN }}

  # ═══════════════════════════════════════════════════════════
  #  阶段 3: 编译 Node.js native addon(含 ARM64)
  # ═══════════════════════════════════════════════════════════
  build-node:
    name: Build Node addon (${{ matrix.name }})
    runs-on: ${{ matrix.runner }}
    needs: version-gate
    if: needs.version-gate.outputs.npm_should_publish == 'true'
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Linux x86_64
            runner: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - name: Linux ARM64
            runner: ubuntu-latest
            target: aarch64-unknown-linux-gnu
          - name: Windows x86_64
            runner: windows-latest
            target: x86_64-pc-windows-msvc
          - name: macOS x86_64
            runner: macos-latest  # Apple Silicon 交叉编译 x86_64
            target: x86_64-apple-darwin
          - name: macOS ARM64
            runner: macos-latest
            target: aarch64-apple-darwin
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      # Linux ARM64 交叉编译需要 gcc-aarch64
      - name: Install cross-compiler (Linux ARM64)
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |

          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Install dependencies
        run: npm install

      - name: Build node module
        run: npx napi build --platform --release --features nodejs --target ${{ matrix.target }}
        env:
          # Linux ARM64 交叉编译需要指定链接器
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }}

      - name: Upload node binding
        uses: actions/upload-artifact@v4
        with:
          name: node-${{ matrix.name }}
          path: "*.node"

  # ═══════════════════════════════════════════════════════════
  #  阶段 4: 发布到 NPM
  # ═══════════════════════════════════════════════════════════
  release-npm:
    name: Release to NPM
    runs-on: ubuntu-latest
    needs: [version-gate, build-node]
    if: needs.version-gate.outputs.npm_should_publish == 'true'
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: 'https://registry.npmjs.org'

      - name: Download all bindings
        uses: actions/download-artifact@v4
        with:
          path: .
          pattern: node-*
          merge-multiple: true

      # 万能入口:运行时自动加载对应平台的二进制
      - name: Generate JS Loader
        run: |

          cat << 'EOF' > index.js
          const { platform, arch } = process;
          let nativeBinding = null;
          try {
            if (platform === 'win32') { nativeBinding = require('./triviumdb.win32-x64-msvc.node'); }
            else if (platform === 'darwin' && arch === 'arm64') { nativeBinding = require('./triviumdb.darwin-arm64.node'); }
            else if (platform === 'darwin') { nativeBinding = require('./triviumdb.darwin-x64.node'); }
            else if (platform === 'linux' && arch === 'arm64') { nativeBinding = require('./triviumdb.linux-arm64-gnu.node'); }
            else { nativeBinding = require('./triviumdb.linux-x64-gnu.node'); }
          } catch (e) {
            console.error(`TriviumDB failed to load native binding for ${platform} ${arch}`, e);
            throw e;
          }
          module.exports = nativeBinding;
          EOF

      - name: Publish to NPM
        run: |

          npm install
          npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  # ═══════════════════════════════════════════════════════════
  #  阶段 5: 发布摘要(无论跳过还是发布都输出结果)
  # ═══════════════════════════════════════════════════════════
  summary:
    name: Release Summary
    runs-on: ubuntu-latest
    needs: [version-gate, release, release-npm]
    if: always()
    steps:
      - name: 输出发布摘要
        run: |

          echo "## 📦 Release Summary: v${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          if [ "${{ needs.version-gate.outputs.pypi_should_publish }}" = "true" ]; then
            echo "- **PyPI**: ✅ 已发布 triviumdb==${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          else
            echo "- **PyPI**: ⏭️ 跳过(版本已存在)" >> $GITHUB_STEP_SUMMARY
          fi
          if [ "${{ needs.version-gate.outputs.npm_should_publish }}" = "true" ]; then
            echo "- **NPM**: ✅ 已发布 triviumdb@${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          else
            echo "- **NPM**: ⏭️ 跳过(版本已存在)" >> $GITHUB_STEP_SUMMARY
          fi