vantadb 0.1.4

VantaDB: An embedded persistent memory and vector retrieval engine for local-first AI applications.
Documentation
name: Python Wheels

on:
  workflow_dispatch:
    inputs:
      publish_testpypi:
        description: "Upload built wheels to TestPyPI"
        required: false
        default: "false"
  pull_request:
    branches: ["main"]
    paths:
      - "src/**"
      - "vantadb-python/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - ".github/workflows/python_wheels.yml"
  push:
    branches: ["main"]
    tags: ["v*.*.*"]
    paths:
      - "src/**"
      - "vantadb-python/**"
      - "Cargo.toml"
      - "Cargo.lock"
      - ".github/workflows/python_wheels.yml"

permissions:
  contents: read

concurrency:
  group: python-wheels-${{ github.ref }}
  cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0

jobs:
  build-wheels:
    name: Build Python wheel (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    timeout-minutes: 45

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.11"

      - name: Set up Rust
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: stable

      - name: Rust cache
        uses: Swatinem/rust-cache@v2
        with:
          cache-bin: "false"

      - name: Install Python build tools
        run: python -m pip install --upgrade pip pytest

      - name: Validate version coherence
        run: cargo test --test version_coherence --no-default-features --features fjall

      - name: Build wheel
        uses: PyO3/maturin-action@v1.51.0
        with:
          command: build
          args: --release --out dist --manifest-path ./vantadb-python/Cargo.toml
          manylinux: 2_28
          before-script-linux: |
            yum install -y clang clang-devel

      - name: Install & smoke test (Unix)
        if: matrix.os != 'windows-latest'
        shell: bash
        run: |
          wheel="$(ls -t ./dist/vantadb_py-*.whl | head -n 1)"
          echo "Installing wheel: $wheel"
          python -m venv /tmp/.smoke-venv
          /tmp/.smoke-venv/bin/pip install --upgrade pip
          /tmp/.smoke-venv/bin/pip install "$wheel" pytest numpy
          /tmp/.smoke-venv/bin/python -c "import vantadb_py; print('Import OK:', vantadb_py.__version__)"
          /tmp/.smoke-venv/bin/pytest vantadb-python/tests/test_sdk.py -v

      - name: Install & smoke test (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          $wheel = (Get-ChildItem .\dist\vantadb_py-*.whl | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
          Write-Output "Installing wheel: $wheel"
          python -m venv "$env:TEMP\.smoke-venv"
          & "$env:TEMP\.smoke-venv\Scripts\pip" install --upgrade pip
          & "$env:TEMP\.smoke-venv\Scripts\pip" install $wheel pytest numpy
          & "$env:TEMP\.smoke-venv\Scripts\python" -c "import vantadb_py; print('Import OK:', vantadb_py.__version__)"
          & "$env:TEMP\.smoke-venv\Scripts\pytest" vantadb-python/tests/test_sdk.py -v

      - name: Upload wheel artifact
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}
          path: ./dist/*.whl
          if-no-files-found: error

  publish-testpypi:
    name: Publish wheels to TestPyPI
    runs-on: ubuntu-latest
    needs: build-wheels
    timeout-minutes: 10
    if: |
      (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
      (github.event_name == 'workflow_dispatch' && inputs.publish_testpypi == 'true')
    environment: testpypi
    permissions:
      id-token: write

    steps:
      - name: Download wheels
        uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          path: ./dist
          merge-multiple: true

      - name: Publish to TestPyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          verbose: true
          skip-existing: true

  publish-pypi:
    name: Publish wheels to PyPI (Production)
    runs-on: ubuntu-latest
    needs: build-wheels
    timeout-minutes: 10
    if: startsWith(github.ref, 'refs/tags/v')
    environment: pypi
    permissions:
      id-token: write
      contents: write
      attestations: write

    steps:
      - name: Download wheels
        uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          path: ./dist
          merge-multiple: true

      - name: Attest Build Provenance for Wheels
        uses: actions/attest-build-provenance@v4
        with:
          subject-path: './dist/*.whl'

      - name: Attach wheel artifacts to GitHub Release
        uses: softprops/action-gh-release@v3
        with:
          files: |
            ./dist/*.whl

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist/
          verbose: true

  verify-testpypi-install:
    name: Verify TestPyPI install
    runs-on: ubuntu-latest
    needs: publish-testpypi
    timeout-minutes: 10
    if: |
      (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
      (github.event_name == 'workflow_dispatch' && inputs.publish_testpypi == 'true')

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.11"

      - name: Extract version from tag or Cargo.toml
        id: pkg_version
        shell: bash
        run: |
          if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
            echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
          else
            version=$(grep '^version' Cargo.toml | head -1 | sed 's/.*= *"//' | sed 's/".*//')
            echo "version=${version}" >> "$GITHUB_OUTPUT"
          fi

      - name: Wait for TestPyPI CDN propagation
        run: sleep 30

      - name: Install from TestPyPI
        run: |
          python -m pip install \
            --index-url https://test.pypi.org/simple/ \
            --extra-index-url https://pypi.org/simple/ \
            "vantadb-py==${{ steps.pkg_version.outputs.version }}"

      - name: Functional smoke test - verify published wheel
        env:
          VANTADB_EXPECTED_VERSION: ${{ steps.pkg_version.outputs.version }}
        run: python vantadb-python/verify_published_wheel.py

  verify-pypi-install:
    name: Verify PyPI production install + Attest provenance
    runs-on: ubuntu-latest
    needs: publish-pypi
    timeout-minutes: 15
    if: startsWith(github.ref, 'refs/tags/v')
    permissions:
      id-token: write
      attestations: read

    steps:
      - name: Checkout (for attestation verification)
        uses: actions/checkout@v6

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.11"

      - name: Extract version from tag
        id: pkg_version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

      - name: Wait for PyPI CDN propagation
        run: |
          echo "Waiting 90 seconds for PyPI CDN propagation..."
          sleep 90

      - name: Install from PyPI (clean environment)
        run: |
          python -m pip install --no-cache-dir \
            "vantadb-py==${{ steps.pkg_version.outputs.version }}"

      - name: Functional smoke test - verify published wheel
        env:
          VANTADB_EXPECTED_VERSION: ${{ steps.pkg_version.outputs.version }}
        run: python vantadb-python/verify_published_wheel.py

      - name: Verify build provenance (GitHub Attestations)
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          # Download the wheel that was just installed to verify its provenance
          pip download --no-deps --dest /tmp/verify_whl \
            "vantadb-py==${{ steps.pkg_version.outputs.version }}"
          wheel_file=$(ls /tmp/verify_whl/vantadb_py-*.whl | head -1)
          echo "Verifying provenance for: ${wheel_file}"
          gh attestation verify "${wheel_file}" \
            --repo "${{ github.repository }}" \
            --format json | python -c "
          import sys, json
          data = json.load(sys.stdin)
          verifications = data if isinstance(data, list) else [data]
          assert len(verifications) > 0, 'No attestation found'
          print(f'Attestations verified: {len(verifications)}')
          print('Build provenance attestation: VALID')
          "
          echo "Supply-chain integrity: VERIFIED"