Continuous Integration for Geode

Continuous Integration (CI) is the practice of automatically building, testing, and validating code changes as they’re committed to version control. For Geode, CI ensures that every change maintains database correctness, preserves GQL standards compliance, prevents performance regressions, and validates polyglot client compatibility.

Foundations of Continuous Integration

Continuous Integration is built on several core principles:

Frequent Integration: Developers integrate code changes multiple times daily, preventing large merge conflicts and integration issues.

Automated Building: Every commit triggers an automated build process that compiles the codebase and identifies compilation errors immediately.

Automated Testing: Comprehensive test suites run automatically, providing rapid feedback on functionality and preventing regressions.

Fast Feedback: Developers receive build and test results within minutes, enabling quick fixes before context switching.

Maintain a Single Source Repository: All code, tests, and configuration reside in version control (Git), ensuring reproducibility.

For database systems like Geode, CI is particularly critical because bugs can result in data corruption, lost transactions, or query correctness issues that are difficult to debug in production.

Geode’s CI Implementation

Repository Structure

Geode’s monorepo structure enables comprehensive CI across all components:

/home/benji/src/gl/devnw/codepros/geode/
├── geode/                    # Server (Zig)
│   ├── src/
│   ├── test/
│   └── build.zig
├── geode-client-go/          # Go client
├── geode-client-python/      # Python client
├── geode-client-rust/        # Rust client
├── geode-client-zig/         # Zig client
├── geode-test-harness/       # Cross-client tests
└── .github/workflows/        # CI configuration

This structure allows CI to:

  • Build all components in a single pipeline
  • Test server changes against all clients
  • Validate cross-client compatibility
  • Ensure end-to-end functionality

Build Process

Server Build (Zig):

# geode/Makefile
build:
	zig build -Doptimize=ReleaseSafe

test:
	zig build test

geodetestlab-comprehensive:
	zig build test
	# Run comprehensive test suite (1,688 tests)
	./zig-out/bin/geodetestlab --comprehensive

CI Build Configuration:

# .github/workflows/build.yml
name: Build Geode

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

jobs:
  build-zig:
    name: Build Server (Zig ${{ matrix.zig }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-24.04, macos-14]
        zig: [0.1.0]
    steps:
      - uses: actions/checkout@v4

      - name: Setup Zig
        uses: goto-bus-stop/setup-zig@v2
        with:
          version: ${{ matrix.zig }}

      - name: Build Geode
        working-directory: geode
        run: |
          zig build -Doptimize=ReleaseSafe
          ./zig-out/bin/geode --version          

      - name: Verify Binary
        run: |
          file geode/zig-out/bin/geode
          ldd geode/zig-out/bin/geode || true          

      - name: Upload Binary
        uses: actions/upload-artifact@v4
        with:
          name: geode-${{ matrix.os }}-${{ matrix.zig }}
          path: geode/zig-out/bin/geode
          retention-days: 7

Automated Testing

Geode’s CI runs multiple test suites:

1. Unit Tests (Core functionality):

unit-tests:
  name: Unit Tests
  runs-on: ubuntu-24.04
  needs: build-zig
  steps:
    - uses: actions/checkout@v4

    - name: Setup Zig
      uses: goto-bus-stop/setup-zig@v2
      with:
        version: 0.1.0

    - name: Run Unit Tests
      working-directory: geode
      run: |
        zig build test -Dtest-filter="*"        

    - name: Parse Test Results
      run: |
        # Extract test counts
        echo "Tests run, passed, failed"        

2. GeodeTestLab Comprehensive Suite (1,688 tests):

    - name: Run GeodeTestLab
      working-directory: geode
      run: |
        make geodetestlab-comprehensive        

    - name: Check Pass Rate
      run: |
        # Require 97.4% pass rate (1644/1688)
        python scripts/check-pass-rate.py \
          --results geode/test-results.json \
          --threshold 0.974        

3. GQL Compliance Tests (ISO/IEC 39075:2024):

gql-compliance:
  name: GQL Compliance
  needs: build-zig
  steps:
    - name: Download Server Binary
      uses: actions/download-artifact@v4

    - name: Start Geode
      run: |
        chmod +x geode
        ./geode serve --listen 0.0.0.0:3141 &
        sleep 5        

    - name: Run Conformance Profile Tests
      run: |
        # Must pass profile scope (see conformance profile)
        python gql/iso-tests/run-suite.py \
          --host localhost:3141 \
          --strict        

    - name: Enforce Conformance Profile
      run: |
        if [ $? -ne 0 ]; then
          echo "GQL conformance profile tests failed - blocking merge"
          exit 1
        fi        

4. Client Library Tests:

client-tests:
  name: Test ${{ matrix.client }} Client
  needs: build-zig
  strategy:
    matrix:
      client: [go, python, rust, zig]
  steps:
    - name: Download Server
      uses: actions/download-artifact@v4

    - name: Start Server
      run: |
        ./geode serve &
        sleep 5        

    - name: Setup ${{ matrix.client }}
      uses: ./.github/actions/setup-${{ matrix.client }}

    - name: Run Tests
      working-directory: geode-client-${{ matrix.client }}
      run: make test

    - name: Upload Results
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: test-results-${{ matrix.client }}
        path: geode-client-${{ matrix.client }}/test-results/

5. Cross-Client Integration Tests:

integration-tests:
  name: Integration Tests
  needs: client-tests
  steps:
    - name: Setup Test Harness
      working-directory: geode-test-harness
      run: make setup

    - name: Run All Client Tests
      run: |
        make test-all
        make test-all-html        

    - name: Upload HTML Report
      uses: actions/upload-artifact@v4
      with:
        name: integration-report
        path: geode-test-harness/reports/

Code Quality Checks

Linting and Formatting:

code-quality:
  name: Code Quality
  runs-on: ubuntu-24.04
  steps:
    - uses: actions/checkout@v4

    - name: Check Zig Formatting
      run: |
        find geode/src -name "*.zig" | xargs zig fmt --check        

    - name: Lint Go Client
      working-directory: geode-client-go
      run: |
        go fmt ./...
        go vet ./...
        golangci-lint run        

    - name: Lint Python Client
      working-directory: geode-client-python
      run: |
        black --check .
        pylint geode_client/
        mypy geode_client/        

    - name: Lint Rust Client
      working-directory: geode-client-rust
      run: |
        cargo fmt -- --check
        cargo clippy -- -D warnings        

Static Analysis:

    - name: Run Static Analysis
      run: |
        # Memory safety checks
        zig build -Dcheck

        # Security scanning
        semgrep --config=auto geode/src/

        # Dependency audit
        cd geode-client-go && go list -m all | nancy sleuth
        cd geode-client-rust && cargo audit        

CANARY Marker Validation:

    - name: Validate CANARY Markers
      run: |
        # Ensure all implementations have CANARY markers
        python scripts/validate-canary-markers.py \
          --dir geode/src \
          --require-evidence        

Advanced CI Patterns

Parallel Test Execution

Geode parallelizes tests for faster feedback:

test-parallel:
  name: Parallel Tests
  runs-on: ubuntu-24.04
  strategy:
    fail-fast: false
    matrix:
      shard: [1, 2, 3, 4, 5, 6, 7, 8]
  steps:
    - name: Run Test Shard ${{ matrix.shard }}
      run: |
        # Split tests into 8 shards
        zig build test -Dshard=${{ matrix.shard }} -Dtotal-shards=8        

Benefits:

  • 8-minute test suite runs in 1-2 minutes
  • Early failure detection
  • Better resource utilization

Incremental Testing

Only test affected components:

    - name: Detect Changed Files
      id: changed
      run: |
        # Get changed files since last commit
        CHANGED=$(git diff --name-only HEAD~1 HEAD)
        echo "files=$CHANGED" >> $GITHUB_OUTPUT        

    - name: Run Targeted Tests
      run: |
        if echo "${{ steps.changed.outputs.files }}" | grep -q "geode/src/parser"; then
          # Parser changed - run parser tests
          zig build test -Dtest-filter="parser.*"
        fi

        if echo "${{ steps.changed.outputs.files }}" | grep -q "geode-client-python"; then
          # Python client changed
          cd geode-client-python && pytest
        fi        

Dependency Caching

Cache dependencies to speed up builds:

    - name: Cache Zig Dependencies
      uses: actions/cache@v4
      with:
        path: |
          ~/.cache/zig
          geode/zig-cache
          geode/zig-out          
        key: ${{ runner.os }}-zig-${{ hashFiles('**/build.zig.zon') }}
        restore-keys: |
          ${{ runner.os }}-zig-          

    - name: Cache Go Modules
      uses: actions/cache@v4
      with:
        path: ~/go/pkg/mod
        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}

    - name: Cache Rust Dependencies
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          geode-client-rust/target          
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

Test Result Reporting

Generate comprehensive test reports:

    - name: Generate Test Report
      if: always()
      run: |
        python scripts/generate-test-report.py \
          --input test-results/ \
          --output test-report.html \
          --format html        

    - name: Publish Test Results
      uses: EnricoMi/publish-unit-test-result-action@v2
      if: always()
      with:
        files: |
          geode/test-results/**/*.xml
          geode-client-*/test-results/**/*.xml
          geode-test-harness/reports/**/*.xml          

    - name: Comment PR with Results
      if: github.event_name == 'pull_request'
      uses: actions/github-script@v7
      with:
        script: |
          const fs = require('fs');
          const report = fs.readFileSync('test-report.html', 'utf8');
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: report
          });          

CI Best Practices for Geode

1. Fail Fast

Prioritize quick checks first:

jobs:
  # Run fast checks first
  quick-checks:
    runs-on: ubuntu-24.04
    steps:
      - name: Lint (30 seconds)
      - name: Format Check (20 seconds)
      - name: Compile Check (1 minute)

  # Run expensive tests only if quick checks pass
  comprehensive-tests:
    needs: quick-checks
    steps:
      - name: Full Test Suite (8 minutes)
      - name: Performance Tests (15 minutes)

2. Keep CI Fast

Target: Feedback within 10 minutes

Optimization strategies:

  • Cache aggressively (builds, dependencies, test data)
  • Parallelize independent tests
  • Use incremental testing
  • Run expensive tests nightly instead of per-commit
on:
  push:
    branches: [main]
  pull_request:
    # Fast tests on PR
    types: [opened, synchronize]
  schedule:
    # Comprehensive tests nightly
    - cron: '0 2 * * *'

3. Make CI Reproducible

Pin all versions:

env:
  ZIG_VERSION: 0.1.0
  GO_VERSION: 1.24.0
  PYTHON_VERSION: 3.11.8
  RUST_VERSION: 1.70.0

  # Pin action versions
  CHECKOUT_VERSION: v4
  SETUP_GO_VERSION: v5

Use deterministic builds:

# Zig builds are deterministic by default
zig build -Doptimize=ReleaseSafe

# Go builds with reproducibility
go build -trimpath -ldflags="-buildid="

4. Test in Clean Environments

Avoid environment pollution:

    - name: Run Tests in Clean Environment
      run: |
        # Each test gets fresh database
        for test in tests/*; do
          # Clean state
          rm -rf /tmp/geode-test-data
          mkdir -p /tmp/geode-test-data

          # Run isolated
          GEODE_DATA_DIR=/tmp/geode-test-data $test
        done        

5. Monitor CI Health

Track CI metrics:

# scripts/ci-metrics.py
from datetime import datetime
from prometheus_client import Counter, Histogram, push_to_gateway

ci_duration = Histogram('ci_duration_seconds', 'CI duration', ['job'])
ci_failures = Counter('ci_failures_total', 'CI failures', ['job'])

def report_ci_metrics(job_name, duration, success):
    ci_duration.labels(job=job_name).observe(duration)
    if not success:
        ci_failures.labels(job=job_name).inc()

    push_to_gateway('monitoring.geodedb.com:9091',
                   job='ci-metrics',
                   registry=registry)

Debugging CI Failures

Access Build Logs

# Download logs from GitHub Actions
gh run view <run-id> --log

# Or via API
curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://api.github.com/repos/devnw/geode/actions/runs/<run-id>/logs

Reproduce Locally

Use act to run GitHub Actions locally:

# Install act
brew install act  # macOS
# or
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | bash

# Run specific job
act -j unit-tests

# Run entire workflow
act push

Debug Mode

Enable debug logging:

    - name: Enable Debug Logging
      if: runner.debug
      run: |
        export GEODE_LOG_LEVEL=debug
        export GEODE_TRACE=1
        zig build test -Dlog-level=debug        

Trigger with:

gh workflow run ci.yml -f debug=true

Common CI Issues

Issue 1: Flaky Tests

# Mark flaky tests with retries
@pytest.mark.flaky(reruns=3, reruns_delay=1)
async def test_concurrent_transactions():
    # Test that occasionally fails due to timing
    pass

Issue 2: Resource Exhaustion

    - name: Clean Up Resources
      if: always()
      run: |
        # Stop all Geode processes
        pkill -9 geode || true

        # Clean temp data
        rm -rf /tmp/geode-*

        # Free memory
        sync; echo 3 > /proc/sys/vm/drop_caches || true        

Issue 3: Timeout Issues

jobs:
  test:
    timeout-minutes: 30  # Prevent hanging builds
    steps:
      - name: Run Tests
        timeout-minutes: 20  # Per-step timeout
        run: make test

CI Security Considerations

Secret Management

    - name: Configure Secrets
      env:
        GEODE_LICENSE_KEY: ${{ secrets.GEODE_LICENSE_KEY }}
        AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
      run: |
        # Never echo secrets
        # Never log secrets
        # Use secrets only where needed        

Dependency Scanning

    - name: Scan Dependencies
      run: |
        # Scan for vulnerabilities
        cd geode-client-go && govulncheck ./...
        cd geode-client-rust && cargo audit
        cd geode-client-python && safety check        

Code Scanning

    - name: CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        languages: go, python, cpp
        queries: security-and-quality

Further Reading

  • CI Best Practices: /docs/development/ci-best-practices/
  • Test Automation Guide: /docs/development/test-automation/
  • GitHub Actions Reference: /docs/operations/github-actions/
  • Build Optimization: /docs/development/build-optimization/
  • Quality Assurance: /docs/development/quality-assurance/

Related Articles

No articles found with this tag yet.

Back to Home