rustberg 0.0.5

A production-grade, cross-platform, single-binary Apache Iceberg REST Catalog
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Cancel in-progress runs for the same branch
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: -Dwarnings
  # Share cache across jobs with same lockfile
  CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse

jobs:
  # Fast checks first - these gate everything else
  quick-checks:
    name: Quick Checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
        with:
          cache-on-failure: true
          # Shared cache key for all ubuntu jobs
          shared-key: ubuntu-cargo
      - name: Rustfmt
        run: cargo fmt --all -- --check
      - name: Clippy
        run: cargo clippy --all-targets --all-features -- -D warnings
      - name: Documentation
        run: cargo doc --all-features --no-deps
        env:
          RUSTDOCFLAGS: -Dwarnings

  # Build once, test/reuse everywhere
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [quick-checks]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          cache-on-failure: true
          shared-key: ubuntu-cargo
      - name: Build debug binary
        run: cargo build --all-features
      - name: Upload debug binary
        uses: actions/upload-artifact@v4
        with:
          name: rustberg-debug
          path: target/debug/rustberg
          retention-days: 1

  test-linux:
    name: Test (Linux)
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: ubuntu-cargo
          save-if: false  # Don't save, build job already did
      - name: Run tests
        run: cargo test --all-features -- --skip trino_
      - name: Run tests (no default features)
        run: cargo test --no-default-features

  # Trino integration tests - runs once using pre-built binary
  test-trino-integration:
    name: Trino Integration
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: ubuntu-cargo
          save-if: false
      - name: Run Trino integration tests
        run: cargo test --all-features trino_

  test-macos:
    name: Test (macOS)
    runs-on: macos-latest
    needs: [quick-checks]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: macos-cargo
      - name: Run tests
        run: cargo test --all-features -- --skip test_aws_kms_ --skip test_vault_kms_ --skip trino_

  test-windows:
    name: Test (Windows)
    runs-on: windows-latest
    needs: [quick-checks]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: windows-cargo
      - name: Run tests
        run: cargo test --all-features -- --skip test_aws_kms_ --skip test_vault_kms_ --skip trino_

  # Python integration tests use pre-built binary
  test-python-integrations:
    name: Python Integrations
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: rustberg-debug
          path: target/debug
      - name: Make binary executable and verify
        run: |
          chmod +x target/debug/rustberg
          ls -la target/debug/
          ./target/debug/rustberg --version
          ./target/debug/rustberg --help | head -5
      - uses: astral-sh/setup-uv@v4
        with:
          version: "latest"
      - name: Install Python dependencies
        run: uv sync
      - name: Run PyIceberg tests
        run: uv run pytest tests/python/test_pyiceberg -v --tb=short
      - name: Run DuckDB tests
        run: uv run pytest tests/python/test_duckdb -v --tb=short -m duckdb

  build-release:
    name: Build Release (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    needs: [test-linux, test-trino-integration]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-musl
            artifact: rustberg
            musl: true
          - os: ubuntu-latest
            target: aarch64-unknown-linux-musl
            artifact: rustberg
            zigbuild: true
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact: rustberg
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact: rustberg.exe
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: ${{ matrix.os }}-release-${{ matrix.target }}
      - name: Install musl-tools
        if: matrix.musl
        run: sudo apt-get update && sudo apt-get install -y musl-tools
      - name: Install Zig
        if: matrix.zigbuild
        uses: mlugg/setup-zig@v1
        with:
          version: 0.13.0
      - name: Install cargo-zigbuild
        if: matrix.zigbuild
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-zigbuild
      - name: Build release (zigbuild)
        if: matrix.zigbuild
        run: cargo zigbuild --release --all-features --target ${{ matrix.target }}
      - name: Build release (native)
        if: ${{ !matrix.zigbuild }}
        run: cargo build --release --all-features --target ${{ matrix.target }}
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: rustberg-${{ matrix.target }}
          path: target/${{ matrix.target }}/release/${{ matrix.artifact }}

  docker:
    name: Docker Build
    runs-on: ubuntu-latest
    needs: [test-linux, test-trino-integration]
    steps:
      - uses: actions/checkout@v4
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build Docker image (multi-arch)
        uses: docker/build-push-action@v6
        with:
          context: .
          push: false
          platforms: linux/amd64,linux/arm64
          tags: rustberg:test
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Test Docker image
        run: |
          docker buildx build --platform linux/amd64 --load -t rustberg:test-amd64 .
          docker run --rm rustberg:test-amd64 --version
          docker run --rm rustberg:test-amd64 --help

  security-audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  helm-lint:
    name: Helm Chart Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Helm
        uses: azure/setup-helm@v4
        with:
          version: 'v3.14.0'
      - name: Lint Helm chart
        run: helm lint charts/rustberg
      - name: Template Helm chart
        run: helm template rustberg charts/rustberg > /dev/null
      - name: Validate Helm chart
        run: |
          helm template rustberg charts/rustberg | kubectl apply --dry-run=client -f - 2>/dev/null || true

  benchmarks:
    name: Benchmarks
    runs-on: ubuntu-latest
    needs: [test-linux, test-trino-integration]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: ubuntu-cargo
          save-if: false
      - name: Install tools
        run: sudo apt-get update && sudo apt-get install -y hyperfine valgrind
      - name: Build release
        run: cargo build --release --all-features
      - name: Measure startup time
        run: |
          echo "=== Startup Time Benchmark ==="
          hyperfine --warmup 3 --runs 10 './target/release/rustberg --help' --export-markdown startup-bench.md
          cat startup-bench.md
      - name: Measure binary size
        run: |
          echo "=== Binary Size ==="
          ls -lh target/release/rustberg
          du -h target/release/rustberg
      - name: Check for memory leaks
        run: valgrind --leak-check=full --error-exitcode=1 ./target/release/rustberg generate-config > /dev/null

  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    needs: [quick-checks]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: ubuntu-cargo
          save-if: false
      - uses: taiki-e/install-action@v2
        with:
          tool: grcov
      - name: Run tests with coverage
        run: |
          CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo test --all-features -- --skip trino_
      - name: Generate coverage report
        run: |
          grcov . --binary-path ./target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o coverage.lcov
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: coverage.lcov
          fail_ci_if_error: false