Skip to content

Coverage

Coverage #20

Workflow file for this run

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
});