Skip to content

LF-12212 Adapt git action (ensure test coverage) to run only when needed #1305

LF-12212 Adapt git action (ensure test coverage) to run only when needed

LF-12212 Adapt git action (ensure test coverage) to run only when needed #1305

name: Enforce Min Test Coverage
# - will make sure that (Foundry) unit test coverage is above min threshold
# - we start with 74% (status today), planning to increase to 100% until EOY 2024
# - Only the 'lines' coverage counts as 'branch' coverage is not reliable
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
enforce-min-test-coverage:
runs-on: ubuntu-latest
# will only run once the PR is in "Ready for Review" state
if: ${{ github.event.pull_request.draft == false }}
permissions:
pull-requests: write
contents: read
env:
ETH_NODE_URI_MAINNET: ${{ secrets.ETH_NODE_URI_MAINNET }}
# keep the rest in alphabetic order
ETH_NODE_URI_ARBITRUM: ${{ secrets.ETH_NODE_URI_ARBITRUM }}
ETH_NODE_URI_BASE: ${{ secrets.ETH_NODE_URI_BASE }}
ETH_NODE_URI_BSC: ${{ secrets.ETH_NODE_URI_BSC }}
ETH_NODE_URI_GNOSIS: ${{ secrets.ETH_NODE_URI_GNOSIS }}
ETH_NODE_URI_GOERLI: ${{ secrets.ETH_NODE_URI_GOERLI }}
ETH_NODE_URI_POLYGON: ${{ secrets.ETH_NODE_URI_POLYGON }}
MIN_TEST_COVERAGE: ${{ secrets.MIN_TEST_COVERAGE }}
steps:
- uses: actions/[email protected]
with:
fetch-depth: 0
- name: Check for Solidity file changes
id: check_files
run: |
git fetch origin ${{ github.base_ref }} --depth=1
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '^src/.*\.sol|^test/.*\.sol' || true)
if [[ -n "$CHANGED_FILES" ]]; then
echo "solidity_changed=true" >> "$GITHUB_ENV"
else
echo "solidity_changed=false" >> "$GITHUB_ENV"
fi
- name: Set up Node.js
uses: actions/[email protected]
with:
node-version: '20'
if: env.solidity_changed == 'true'
- name: Install dev dependencies
run: yarn install
if: env.solidity_changed == 'true'
- name: Install Foundry
uses: foundry-rs/[email protected]
if: env.solidity_changed == 'true'
- name: Install Dependencies
run: forge install
if: env.solidity_changed == 'true'
- name: Generate Coverage Report
if: env.solidity_changed == 'true'
run: |
forge coverage --report lcov --force --evm-version 'shanghai' --ir-minimum
echo "Filtering coverage report to only contain coverage info for 'src/'' folder now"
npx tsx script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/'
echo "Coverage report successfully filtered"
- name: Generate Coverage Summary
if: env.solidity_changed == 'true'
run: |
# Path to the lcov info file
LCOV_FILE="lcov-filtered.info"
# Initialize counters
TOTAL_LINES_FOUND=0
TOTAL_LINES_HIT=0
TOTAL_FUNCTIONS_FOUND=0
TOTAL_FUNCTIONS_HIT=0
TOTAL_BRANCHES_FOUND=0
TOTAL_BRANCHES_HIT=0
# Read through the lcov file
while IFS= read -r line; do
case $line in
LF:*)
TOTAL_LINES_FOUND=$((TOTAL_LINES_FOUND + ${line#LF:}))
;;
LH:*)
TOTAL_LINES_HIT=$((TOTAL_LINES_HIT + ${line#LH:}))
;;
FNF:*)
TOTAL_FUNCTIONS_FOUND=$((TOTAL_FUNCTIONS_FOUND + ${line#FNF:}))
;;
FNH:*)
TOTAL_FUNCTIONS_HIT=$((TOTAL_FUNCTIONS_HIT + ${line#FNH:}))
;;
BRF:*)
TOTAL_BRANCHES_FOUND=$((TOTAL_BRANCHES_FOUND + ${line#BRF:}))
;;
BRH:*)
TOTAL_BRANCHES_HIT=$((TOTAL_BRANCHES_HIT + ${line#BRH:}))
;;
esac
done < "$LCOV_FILE"
# Calculate percentages with high precision
LINE_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_LINES_HIT / $TOTAL_LINES_FOUND * 100" | bc)
FUNCTION_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_FUNCTIONS_HIT / $TOTAL_FUNCTIONS_FOUND * 100" | bc)
BRANCH_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_BRANCHES_HIT / $TOTAL_BRANCHES_FOUND * 100" | bc)
# Format results with two decimal places and alignment
LINE_COVERAGE_PERCENTAGE=$(printf "%.2f" "$LINE_COVERAGE_PERCENTAGE")
FUNCTION_COVERAGE_PERCENTAGE=$(printf "%.2f" "$FUNCTION_COVERAGE_PERCENTAGE")
BRANCH_COVERAGE_PERCENTAGE=$(printf "%.2f" "$BRANCH_COVERAGE_PERCENTAGE")
# Prepare aligned output
LINE_COVERAGE_REPORT=$(printf "Line Coverage: %6s%% (%4d / %4d lines)" "$LINE_COVERAGE_PERCENTAGE" "$TOTAL_LINES_HIT" "$TOTAL_LINES_FOUND")
FUNCTION_COVERAGE_REPORT=$(printf "Function Coverage: %6s%% (%4d / %4d functions)" "$FUNCTION_COVERAGE_PERCENTAGE" "$TOTAL_FUNCTIONS_HIT" "$TOTAL_FUNCTIONS_FOUND")
BRANCH_COVERAGE_REPORT=$(printf "Branch Coverage: %6s%% (%4d / %4d branches)" "$BRANCH_COVERAGE_PERCENTAGE" "$TOTAL_BRANCHES_HIT" "$TOTAL_BRANCHES_FOUND")
# Check against minimum threshold
if (( $(echo "$LINE_COVERAGE_PERCENTAGE >= $MIN_TEST_COVERAGE" | bc -l) )); then
RESULT_COVERAGE_REPORT="Test coverage ($LINE_COVERAGE_PERCENTAGE%) is above min threshold ($MIN_TEST_COVERAGE%). Check passed."
echo -e "\033[32m$RESULT_COVERAGE_REPORT\033[0m"
else
RESULT_COVERAGE_REPORT="Test coverage ($LINE_COVERAGE_PERCENTAGE%) is below min threshold ($MIN_TEST_COVERAGE%). Check failed."
echo -e "\033[31m$RESULT_COVERAGE_REPORT\033[0m"
exit 1
fi
# Output result_COVERAGE_REPORTs
echo "$LINE_COVERAGE_REPORT"
echo "$FUNCTION_COVERAGE_REPORT"
echo "$BRANCH_COVERAGE_REPORT"
echo "$RESULT_COVERAGE_REPORT"
# Store in GitHub environment variables
{
echo "LINE_COVERAGE_REPORT=$LINE_COVERAGE_REPORT"
echo "FUNCTION_COVERAGE_REPORT=$FUNCTION_COVERAGE_REPORT"
echo "BRANCH_COVERAGE_REPORT=$BRANCH_COVERAGE_REPORT"
echo "RESULT_COVERAGE_REPORT=$RESULT_COVERAGE_REPORT"
} >> "$GITHUB_ENV"
- name: Comment with Coverage Summary in PR
if: env.solidity_changed == 'true'
uses: mshick/[email protected]
with:
repo-token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }}
message: |
## Test Coverage Report
${{ env.LINE_COVERAGE_REPORT }}
${{ env.FUNCTION_COVERAGE_REPORT }}
${{ env.BRANCH_COVERAGE_REPORT }}
${{ env.RESULT_COVERAGE_REPORT }}
- name: Skip Tests (No Solidity Changes)
if: env.solidity_changed == 'false'
run: echo "No Solidity files changed. Skipping test coverage check."