# Code Coverage Workflow # # This workflow runs test coverage analysis and uploads reports to Codecov. # Coverage reports help identify untested code paths and maintain code quality. # # What it does: # - Runs unit and integration tests with coverage instrumentation # - Runs E2E tests with coverage instrumentation # - Uploads coverage reports to Codecov (https://codecov.io/gh/nearai/ironclaw) # # Viewing coverage reports: # - PRs automatically get coverage comments showing changes in coverage # - Visit https://codecov.io/gh/nearai/ironclaw for detailed coverage reports # - Coverage reports are generated for three configurations: # 1. all-features: Full feature set # 2. default: Default features # 3. libsql-only: Minimal libSQL-only configuration # - E2E coverage tracks end-to-end test coverage separately # # Coverage files: # - Unit/integration: lcov.info (uploaded to Codecov with "unit" flag) # - E2E: e2e-coverage.info (uploaded to Codecov with "e2e" flag) # # Requirements: # - Uses cargo-llvm-cov for coverage instrumentation # - Requires PostgreSQL for integration tests (pgvector/pgvector:pg16) # - E2E tests require Python 3.12 and Playwright name: Code Coverage on: push: branches: [main] permissions: id-token: write contents: read jobs: coverage: name: Coverage (${{ matrix.name }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - name: all-features flags: "--all-features" has_postgres: true - name: default flags: "" has_postgres: true - name: libsql-only flags: "--no-default-features --features libsql" has_postgres: false services: postgres: image: pgvector/pgvector:pg16 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: ironclaw_test ports: - 5432:5432 options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: llvm-tools-preview targets: wasm32-wasip2 - uses: Swatinem/rust-cache@v2 with: key: coverage-${{ matrix.name }} - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Install cargo-component run: | if ! command -v cargo-component >/dev/null 2>&1; then cargo install cargo-component --locked fi - name: Build WASM channels (for integration tests) run: ./scripts/build-wasm-extensions.sh --channels - name: Run database migrations if: matrix.has_postgres run: | set -euo pipefail readarray -t migration_files < <(printf '%s\n' migrations/V*.sql | sort -V) for f in "${migration_files[@]}"; do echo "Applying $f..." psql -v ON_ERROR_STOP=1 -f "$f" done env: PGHOST: localhost PGUSER: postgres PGPASSWORD: postgres PGDATABASE: ironclaw_test - name: Set DATABASE_URL for postgres configs if: matrix.has_postgres run: echo "DATABASE_URL=postgres://postgres:postgres@localhost/ironclaw_test" >> "$GITHUB_ENV" - name: Generate coverage run: cargo llvm-cov ${{ matrix.flags }} --workspace --lcov --output-path lcov.info - name: Upload to Codecov uses: codecov/codecov-action@v5 with: files: lcov.info flags: ${{ matrix.name }} disable_search: true use_oidc: true fail_ci_if_error: true e2e-coverage: name: E2E Coverage runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: llvm-tools-preview targets: wasm32-wasip2 - uses: Swatinem/rust-cache@v2 with: key: e2e-coverage - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Install cargo-component run: | if ! command -v cargo-component >/dev/null 2>&1; then cargo install cargo-component --locked fi - name: Build WASM channels run: ./scripts/build-wasm-extensions.sh --channels - name: Set up coverage instrumentation run: | # show-env outputs shell-quoted values (KEY='value') but GITHUB_ENV # expects unquoted KEY=value. Strip only the wrapping single quotes # from KEY='value' lines without altering any internal characters. cargo llvm-cov show-env | sed -E "s/^([A-Za-z_][A-Za-z0-9_]*)='(.*)'$/\1=\2/" >> "$GITHUB_ENV" - name: Clean coverage workspace run: cargo llvm-cov clean --workspace - name: Build instrumented binary run: cargo build --no-default-features --features libsql - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install E2E dependencies run: | cd tests/e2e pip install -e . playwright install --with-deps chromium - name: Run E2E tests run: | pytest tests/e2e/ -v -x --timeout=120 env: RUST_LOG: ironclaw=info RUST_BACKTRACE: "1" - name: Verify profraw files exist if: always() run: | echo "LLVM_PROFILE_FILE=${LLVM_PROFILE_FILE}" echo "CARGO_LLVM_COV_TARGET_DIR=${CARGO_LLVM_COV_TARGET_DIR}" profraw_count=$(find target/ -name '*.profraw' 2>/dev/null | wc -l) echo "Found ${profraw_count} .profraw files under target/" find target/ -name '*.profraw' 2>/dev/null || true if [ "$profraw_count" -eq 0 ]; then echo "::warning::No .profraw files found — coverage report will fail" fi - name: Generate coverage report if: always() run: cargo llvm-cov report --lcov --output-path e2e-coverage.info - name: Upload to Codecov if: always() uses: codecov/codecov-action@v5 with: files: e2e-coverage.info flags: e2e disable_search: true use_oidc: true fail_ci_if_error: true - name: Upload screenshots on failure if: failure() uses: actions/upload-artifact@v4 with: name: e2e-screenshots path: tests/e2e/screenshots/ if-no-files-found: ignore coverage-gate: name: Coverage runs-on: ubuntu-latest if: always() needs: [coverage, e2e-coverage] steps: - run: | if [[ "${{ needs.coverage.result }}" != "success" || "${{ needs.e2e-coverage.result }}" != "success" ]]; then echo "One or more coverage jobs failed" exit 1 fi