Coverage #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Coverage | |
| on: | |
| # Only run coverage on release tags to reduce CI/CD load | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| # Manual trigger for testing | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version tag (e.g., v0.4.6)' | |
| required: false | |
| type: string | |
| # Cancel outdated workflow runs | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| # Reduce disk usage during builds | |
| CARGO_INCREMENTAL: 0 | |
| # Minimize debug info to save disk space | |
| CARGO_PROFILE_DEV_DEBUG: 0 | |
| jobs: | |
| coverage: | |
| name: Code Coverage | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free up disk space | |
| run: | | |
| echo "=== Initial disk space ===" | |
| df -h / | |
| echo "" | |
| echo "=== Removing unnecessary packages ===" | |
| # Remove large unnecessary packages | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /usr/local/lib/android | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /opt/hostedtoolcache/CodeQL | |
| sudo apt-get autoremove -y | |
| sudo apt-get clean | |
| echo "" | |
| echo "=== Disk space after cleanup ===" | |
| df -h / | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools-preview | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libpcap-dev pkg-config | |
| # Use Swatinem/rust-cache for optimal Rust caching (same as CI workflow) | |
| - name: Cache dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "coverage" | |
| cache-targets: "false" | |
| cache-on-failure: "false" | |
| - name: Install cargo-tarpaulin | |
| run: | | |
| # Check if already installed to save time | |
| if ! command -v cargo-tarpaulin &> /dev/null; then | |
| cargo install cargo-tarpaulin | |
| else | |
| echo "cargo-tarpaulin already installed" | |
| fi | |
| - name: Generate coverage report | |
| id: tarpaulin | |
| run: | | |
| echo "=== Disk space before coverage ===" | |
| df -h / | |
| # Run tarpaulin with optimized options to prevent hangs: | |
| # - --engine llvm: Use LLVM instrumentation instead of ptrace (prevents breakpoint hangs) | |
| # - --target-dir: Separate directory avoids incremental compilation issues | |
| # - --timeout 300: 5-minute per-test timeout (generous for slow tests) | |
| # - --test-threads 1: Sequential tests prevent race conditions | |
| # See: https://github.com/xd009642/tarpaulin/issues/971 | |
| OUTPUT=$(cargo tarpaulin --workspace \ | |
| --engine llvm \ | |
| --target-dir target-cov \ | |
| --timeout 300 \ | |
| --out Lcov \ | |
| --output-dir coverage \ | |
| --exclude-files "crates/prtip-cli/src/main.rs" \ | |
| --exclude-files "*/tests/*" \ | |
| --exclude-files "*/benches/*" \ | |
| -- --test-threads 1 2>&1) || true | |
| # Display the output | |
| echo "$OUTPUT" | |
| echo "=== Disk space after coverage ===" | |
| df -h / | |
| # Extract coverage percentage from output (format: "XX.XX% coverage, N/M lines covered") | |
| COVERAGE=$(echo "$OUTPUT" | grep -oP '\d+\.\d+(?=% coverage)' | tail -1) | |
| if [ -z "$COVERAGE" ]; then | |
| echo "Warning: Could not extract coverage percentage, defaulting to 0" | |
| COVERAGE="0.0" | |
| fi | |
| echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT | |
| echo "Extracted coverage: $COVERAGE%" | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: coverage/lcov.info | |
| flags: rust | |
| name: codecov-prtip | |
| fail_ci_if_error: false | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: coverage/lcov.info | |
| retention-days: 14 | |
| - name: Extract coverage percentage | |
| id: coverage | |
| run: | | |
| # Get coverage from tarpaulin step output | |
| COVERAGE=${{ steps.tarpaulin.outputs.coverage }} | |
| echo "percentage=$COVERAGE" >> $GITHUB_OUTPUT | |
| echo "Current coverage: $COVERAGE%" | |
| - name: Check coverage threshold | |
| run: | | |
| COVERAGE=${{ steps.coverage.outputs.percentage }} | |
| THRESHOLD=50.0 | |
| echo "Current coverage: $COVERAGE%" | |
| echo "Required threshold: $THRESHOLD%" | |
| # Use awk for floating point comparison (bc not always available) | |
| if awk -v cov="$COVERAGE" -v thr="$THRESHOLD" 'BEGIN {exit !(cov < thr)}'; then | |
| echo "❌ Coverage $COVERAGE% is below threshold $THRESHOLD%" | |
| echo "::error::Coverage regression detected. Current: $COVERAGE%, Required: $THRESHOLD%" | |
| exit 1 | |
| fi | |
| echo "✅ Coverage $COVERAGE% meets threshold $THRESHOLD%" | |
| echo "::notice::Coverage check passed: $COVERAGE% >= $THRESHOLD%" | |
| - name: Comment PR with coverage | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v6 | |
| with: | |
| script: | | |
| const coverage = '${{ steps.coverage.outputs.percentage }}'; | |
| const threshold = '50.0'; | |
| const passed = parseFloat(coverage) >= parseFloat(threshold); | |
| const emoji = passed ? '✅' : '❌'; | |
| const comment = `## ${emoji} Coverage Report | |
| **Current Coverage:** ${coverage}% | |
| **Threshold:** ${threshold}% | |
| **Status:** ${passed ? 'PASSED' : 'FAILED'} | |
| ${passed ? | |
| '✅ Coverage meets the minimum threshold.' : | |
| '❌ Coverage is below the minimum threshold. Please add more tests.'} | |
| 📊 [View detailed coverage report in artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) | |
| `; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); |