Build(deps): Bump urllib3 from 2.7.0 to 2.8.0 in /eng #2983
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: PR Code Coverage | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: pr-code-coverage-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| coverage-report: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 245 | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Setup git for diff-cover | |
| run: | | |
| # Fetch the main branch for comparison | |
| git fetch origin main:main | |
| # Show available branches for debugging | |
| echo "Available branches:" | |
| git branch -a | |
| # Verify main branch exists | |
| git show-ref --verify refs/heads/main || echo "Warning: main branch not found" | |
| git show-ref --verify refs/remotes/origin/main || echo "Warning: origin/main not found" | |
| - name: Wait for ADO build to start | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| PR_BRANCH="refs/pull/$PR_NUMBER/merge" | |
| API_URL="https://dev.azure.com/sqlclientdrivers/public/_apis/build/builds?definitions=2128&branchName=refs%2Fpull%2F${PR_NUMBER}%2Fmerge&queryOrder=queueTimeDescending&%24top=100&api-version=7.1-preview.7" | |
| DEADLINE=$((SECONDS + 15 * 60)) | |
| API_FAILURES=0 | |
| BUILD_ID="" | |
| echo "Waiting up to 15 minutes for Azure DevOps build for PR #$PR_NUMBER at $PR_HEAD_SHA..." | |
| while (( SECONDS < DEADLINE )); do | |
| REQUEST_TIMEOUT=$((DEADLINE - SECONDS)) | |
| if (( REQUEST_TIMEOUT <= 0 )); then break; fi | |
| if (( REQUEST_TIMEOUT > 30 )); then REQUEST_TIMEOUT=30; fi | |
| if API_RESPONSE=$(curl --fail --silent --show-error --connect-timeout 10 --max-time "$REQUEST_TIMEOUT" "$API_URL") && | |
| jq -e 'type == "object" and (.value | type == "array")' <<< "$API_RESPONSE" >/dev/null 2>&1; then | |
| API_FAILURES=0 | |
| else | |
| API_FAILURES=$((API_FAILURES + 1)) | |
| echo "β οΈ Build API HTTP/JSON error ($API_FAILURES/5)" | |
| if (( API_FAILURES >= 5 )); then | |
| echo "β Azure DevOps build API unavailable after 5 consecutive failures" | |
| exit 1 | |
| fi | |
| API_RESPONSE='{"value":[]}' | |
| fi | |
| # The merge ref is shared across revisions; match the actual PR head as well. | |
| BUILD_INFO=$(jq -c --arg PR "$PR_NUMBER" --arg SHA "$PR_HEAD_SHA" --arg BRANCH "$PR_BRANCH" ' | |
| [.value[]? | select( | |
| .definition.id == 2128 and .sourceBranch == $BRANCH and | |
| (.triggerInfo["pr.number"] | tostring) == $PR and | |
| .triggerInfo["pr.sourceSha"] == $SHA | |
| )] | .[0] // empty' <<< "$API_RESPONSE") | |
| if [[ -n "$BUILD_INFO" ]]; then | |
| STATUS=$(echo "$BUILD_INFO" | jq -r '.status // "unknown"') | |
| RESULT=$(echo "$BUILD_INFO" | jq -r '.result // "unknown"') | |
| BUILD_ID=$(echo "$BUILD_INFO" | jq -r '.id // "unknown"') | |
| WEB_URL=$(echo "$BUILD_INFO" | jq -r '._links.web.href // "unknown"') | |
| [[ "$BUILD_ID" =~ ^[0-9]+$ ]] || { | |
| echo "Invalid Azure DevOps build ID" | |
| exit 1 | |
| } | |
| if [[ ${#WEB_URL} -gt 500 || "$WEB_URL" == *$'\n'* || "$WEB_URL" == *$'\r'* ]]; then | |
| echo "Invalid Azure DevOps build URL" | |
| exit 1 | |
| fi | |
| echo "β Found build: ID=$BUILD_ID, Status=$STATUS, Result=$RESULT" | |
| echo "π Build URL: $WEB_URL" | |
| echo "ADO_URL=$WEB_URL" >> "$GITHUB_ENV" | |
| echo "BUILD_ID=$BUILD_ID" >> "$GITHUB_ENV" | |
| echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV" | |
| echo "PR_HEAD_SHA=$PR_HEAD_SHA" >> "$GITHUB_ENV" | |
| # A failed matrix leg does not invalidate a successful coverage artifact. | |
| echo "π Build found, proceeding to poll for coverage artifacts..." | |
| break | |
| fi | |
| echo "β³ No matching build found for PR #$PR_NUMBER at $PR_HEAD_SHA yet..." | |
| SLEEP_SECONDS=$((DEADLINE - SECONDS)) | |
| if (( SLEEP_SECONDS > 30 )); then SLEEP_SECONDS=30; fi | |
| if (( SLEEP_SECONDS > 0 )); then sleep "$SLEEP_SECONDS"; fi | |
| done | |
| if [[ -z "$BUILD_ID" ]]; then | |
| echo "β Timeout: No build found for PR #$PR_NUMBER at $PR_HEAD_SHA within 15 minutes" | |
| exit 1 | |
| fi | |
| - name: Download and parse coverage report | |
| run: | | |
| BUILD_URL="https://dev.azure.com/SqlClientDrivers/public/_apis/build/builds/$BUILD_ID?api-version=7.1-preview.7" | |
| ARTIFACTS_URL="https://dev.azure.com/SqlClientDrivers/public/_apis/build/builds/$BUILD_ID/artifacts?api-version=7.1-preview.5" | |
| BUILDS_URL="https://dev.azure.com/sqlclientdrivers/public/_apis/build/builds?definitions=2128&branchName=refs%2Fpull%2F${PR_NUMBER}%2Fmerge&queryOrder=queueTimeDescending&%24top=100&api-version=7.1-preview.7" | |
| PR_BRANCH="refs/pull/$PR_NUMBER/merge" | |
| # Coverage may queue behind 160-minute benchmark jobs before its own run. | |
| DEADLINE=$((SECONDS + 220 * 60)) | |
| COMPLETED_AT=-1 | |
| ARTIFACT_FAILURES=0 | |
| BUILD_FAILURES=0 | |
| COVERAGE_ARTIFACT="" | |
| COVERAGE_ARTIFACT_APPROVED=false | |
| echo "π₯ Waiting up to 220 minutes for coverage artifacts for build $BUILD_ID..." | |
| while (( SECONDS < DEADLINE )); do | |
| COVERAGE_ARTIFACT="" | |
| REQUEST_TIMEOUT=$((DEADLINE - SECONDS)) | |
| if (( REQUEST_TIMEOUT <= 0 )); then break; fi | |
| if (( REQUEST_TIMEOUT > 30 )); then REQUEST_TIMEOUT=30; fi | |
| if ARTIFACTS_RESPONSE=$(curl --fail --silent --show-error --connect-timeout 10 --max-time "$REQUEST_TIMEOUT" "$ARTIFACTS_URL") && | |
| jq -e 'type == "object" and (.value | type == "array")' <<< "$ARTIFACTS_RESPONSE" >/dev/null 2>&1; then | |
| ARTIFACT_FAILURES=0 | |
| COVERAGE_ARTIFACT=$(jq -r ' | |
| [.value[]? | select(.name | test("Code Coverage Report")) | | |
| .resource.downloadUrl | select(type == "string" and length > 0)] | | |
| .[0] // empty' <<< "$ARTIFACTS_RESPONSE") | |
| else | |
| ARTIFACT_FAILURES=$((ARTIFACT_FAILURES + 1)) | |
| echo "β οΈ Artifacts API HTTP/JSON error ($ARTIFACT_FAILURES/5)" | |
| if (( ARTIFACT_FAILURES >= 5 )); then | |
| echo "β Azure DevOps artifacts API unavailable after 5 consecutive failures" | |
| exit 1 | |
| fi | |
| fi | |
| # Inspect lifecycle, not aggregate result: independent matrix jobs can fail. | |
| REQUEST_TIMEOUT=$((DEADLINE - SECONDS)) | |
| if (( REQUEST_TIMEOUT <= 0 )); then break; fi | |
| if (( REQUEST_TIMEOUT > 30 )); then REQUEST_TIMEOUT=30; fi | |
| if BUILD_RESPONSE=$(curl --fail --silent --show-error --connect-timeout 10 --max-time "$REQUEST_TIMEOUT" "$BUILD_URL") && | |
| jq -e --arg ID "$BUILD_ID" '(.id | tostring) == $ID and | |
| (.status | type == "string")' <<< "$BUILD_RESPONSE" >/dev/null 2>&1; then | |
| BUILD_FAILURES=0 | |
| STATUS=$(jq -r '.status' <<< "$BUILD_RESPONSE") | |
| RESULT=$(jq -r '.result // "unknown"' <<< "$BUILD_RESPONSE") | |
| if [[ "$STATUS" == "completed" && "$RESULT" == "canceled" ]]; then | |
| if REPLACEMENTS=$(curl --fail --silent --show-error --connect-timeout 10 --max-time "$REQUEST_TIMEOUT" "$BUILDS_URL") && | |
| REPLACEMENT=$(jq -ce --arg PR "$PR_NUMBER" --arg SHA "$PR_HEAD_SHA" \ | |
| --arg BRANCH "$PR_BRANCH" --arg ID "$BUILD_ID" ' | |
| [.value[]? | select( | |
| .definition.id == 2128 and .sourceBranch == $BRANCH and | |
| (.triggerInfo["pr.number"] | tostring) == $PR and | |
| .triggerInfo["pr.sourceSha"] == $SHA and | |
| .id > ($ID | tonumber) and | |
| (.status != "completed" or .result != "canceled") | |
| )] | .[0]' <<< "$REPLACEMENTS"); then | |
| BUILD_ID=$(jq -r '.id' <<< "$REPLACEMENT") | |
| [[ "$BUILD_ID" =~ ^[0-9]+$ ]] || { | |
| echo "Invalid replacement Azure DevOps build ID" | |
| exit 1 | |
| } | |
| BUILD_URL="https://dev.azure.com/SqlClientDrivers/public/_apis/build/builds/$BUILD_ID?api-version=7.1-preview.7" | |
| ARTIFACTS_URL="https://dev.azure.com/SqlClientDrivers/public/_apis/build/builds/$BUILD_ID/artifacts?api-version=7.1-preview.5" | |
| ADO_URL="https://dev.azure.com/sqlclientdrivers/public/_build/results?buildId=$BUILD_ID" | |
| echo "BUILD_ID=$BUILD_ID" >> "$GITHUB_ENV" | |
| echo "ADO_URL=$ADO_URL" >> "$GITHUB_ENV" | |
| COMPLETED_AT=-1 | |
| ARTIFACT_FAILURES=0 | |
| BUILD_FAILURES=0 | |
| COVERAGE_ARTIFACT="" | |
| COVERAGE_ARTIFACT_APPROVED=false | |
| echo "Selected ADO run was canceled; continuing with replacement build $BUILD_ID" | |
| continue | |
| fi | |
| ARTIFACT_FAILURES=0 | |
| echo "Canceled build $BUILD_ID has no replacement yet..." | |
| SLEEP_SECONDS=$((DEADLINE - SECONDS)) | |
| if (( SLEEP_SECONDS > 30 )); then SLEEP_SECONDS=30; fi | |
| if (( SLEEP_SECONDS > 0 )); then sleep "$SLEEP_SECONDS"; fi | |
| continue | |
| fi | |
| if [[ -n "$COVERAGE_ARTIFACT" ]] && | |
| { [[ "$STATUS" == "inProgress" ]] || | |
| { [[ "$STATUS" == "completed" ]] && | |
| [[ "$RESULT" =~ ^(succeeded|partiallySucceeded|failed)$ ]]; }; }; then | |
| COVERAGE_ARTIFACT_APPROVED=true | |
| echo "β Found coverage artifact!" | |
| break | |
| fi | |
| if [[ "$STATUS" == "completed" ]] && (( COMPLETED_AT < 0 )); then | |
| COMPLETED_AT=$SECONDS | |
| echo "Build completed ($RESULT); allowing 2 minutes for artifact propagation..." | |
| fi | |
| else | |
| BUILD_FAILURES=$((BUILD_FAILURES + 1)) | |
| echo "β οΈ Build lifecycle API HTTP/JSON error ($BUILD_FAILURES/5)" | |
| if (( BUILD_FAILURES >= 5 )); then | |
| echo "β Azure DevOps build lifecycle API unavailable after 5 consecutive failures" | |
| exit 1 | |
| fi | |
| fi | |
| if (( COMPLETED_AT >= 0 && SECONDS - COMPLETED_AT >= 120 )); then | |
| echo "β Build $BUILD_ID completed but coverage artifact is still unavailable after propagation grace" | |
| exit 1 | |
| fi | |
| echo "β³ Coverage report not ready yet..." | |
| SLEEP_SECONDS=$((DEADLINE - SECONDS)) | |
| if (( SLEEP_SECONDS > 30 )); then SLEEP_SECONDS=30; fi | |
| if (( SLEEP_SECONDS > 0 )); then sleep "$SLEEP_SECONDS"; fi | |
| done | |
| if [[ "$COVERAGE_ARTIFACT_APPROVED" != true || -z "$COVERAGE_ARTIFACT" ]]; then | |
| echo "β Timeout: Coverage report artifact not found within 220 minutes" | |
| exit 1 | |
| fi | |
| if [[ -n "$COVERAGE_ARTIFACT" && "$COVERAGE_ARTIFACT" != "null" && "$COVERAGE_ARTIFACT" != "empty" ]]; then | |
| echo "π Downloading coverage report..." | |
| COVERAGE_ARCHIVE="$RUNNER_TEMP/coverage-report.zip" | |
| if ! curl -L "$COVERAGE_ARTIFACT" -o "$COVERAGE_ARCHIVE" --fail --silent --show-error \ | |
| --connect-timeout 10 --max-time 60 --max-filesize 268435456 \ | |
| --retry 2 --retry-delay 5 --retry-max-time 180; then | |
| echo "β Failed to download coverage report from Azure DevOps" | |
| echo "This indicates the coverage artifacts may not be available or accessible" | |
| exit 1 | |
| fi | |
| INDEX_FILE="$RUNNER_TEMP/coverage-index.html" | |
| if ! python .github/scripts/extract_coverage_artifact.py html "$COVERAGE_ARCHIVE" "$INDEX_FILE"; then | |
| echo "β Failed to read the coverage HTML artifact" | |
| exit 1 | |
| fi | |
| if [[ -f "$INDEX_FILE" ]]; then | |
| echo "π Parsing coverage data from $INDEX_FILE..." | |
| # Debug: Show relevant parts of the HTML | |
| echo "Debug: Looking for coverage data..." | |
| grep -n "cardpercentagebar\|Covered lines\|Coverable lines" "$INDEX_FILE" | head -10 | |
| # Extract coverage metrics using simpler, more reliable patterns | |
| OVERALL_PERCENTAGE=$(grep -o 'cardpercentagebar[0-9]*">[0-9]*%' "$INDEX_FILE" | head -1 | grep -o '[0-9]*%') | |
| COVERED_LINES=$(grep -A1 "Covered lines:" "$INDEX_FILE" | grep -o 'title="[0-9]*"' | head -1 | grep -o '[0-9]*') | |
| TOTAL_LINES=$(grep -A1 "Coverable lines:" "$INDEX_FILE" | grep -o 'title="[0-9]*"' | head -1 | grep -o '[0-9]*') | |
| # Fallback method if the above doesn't work | |
| if [[ -z "$OVERALL_PERCENTAGE" ]]; then | |
| echo "Trying alternative parsing method..." | |
| OVERALL_PERCENTAGE=$(grep -o 'large.*">[0-9]*%' "$INDEX_FILE" | head -1 | grep -o '[0-9]*%') | |
| fi | |
| echo "Extracted values:" | |
| echo "OVERALL_PERCENTAGE=$OVERALL_PERCENTAGE" | |
| echo "COVERED_LINES=$COVERED_LINES" | |
| echo "TOTAL_LINES=$TOTAL_LINES" | |
| # Validate that we got the essential data | |
| if [[ -z "$OVERALL_PERCENTAGE" ]]; then | |
| echo "β Could not extract coverage percentage from the report" | |
| echo "The coverage report format may have changed or be incomplete" | |
| exit 1 | |
| fi | |
| echo "COVERAGE_PERCENTAGE=$OVERALL_PERCENTAGE" >> $GITHUB_ENV | |
| echo "COVERED_LINES=${COVERED_LINES:-N/A}" >> $GITHUB_ENV | |
| echo "TOTAL_LINES=${TOTAL_LINES:-N/A}" >> $GITHUB_ENV | |
| # Extract top files with low coverage - improved approach | |
| echo "π Extracting file-level coverage..." | |
| # Extract file coverage data more reliably | |
| LOW_COVERAGE_FILES=$(grep -o '<td><a href="[^"]*">[^<]*</a></td><td class="right">[0-9]*</td><td class="right">[0-9]*</td><td class="right">[0-9]*</td><td class="right">[0-9]*</td><td title="[^"]*" class="right">[0-9]*\.[0-9]*%' "$INDEX_FILE" | \ | |
| sed 's/<td><a href="[^"]*">\([^<]*\)<\/a><\/td>.*class="right">\([0-9]*\.[0-9]*\)%/\1: \2%/' | \ | |
| sort -t: -k2 -n | head -10) | |
| # Alternative method if above fails | |
| if [[ -z "$LOW_COVERAGE_FILES" ]]; then | |
| echo "Trying alternative file parsing..." | |
| LOW_COVERAGE_FILES=$(grep -E "\.py.*[0-9]+\.[0-9]+%" "$INDEX_FILE" | \ | |
| grep -o "[^>]*\.py[^<]*</a>.*[0-9]*\.[0-9]*%" | \ | |
| sed 's/\([^<]*\)<\/a>.*\([0-9]*\.[0-9]*\)%/\1: \2%/' | \ | |
| sort -t: -k2 -n | head -10) | |
| fi | |
| LOW_COVERAGE_FILES="${LOW_COVERAGE_FILES:-No detailed file data available}" | |
| LOW_COVERAGE_DELIMITER="coverage_$(openssl rand -hex 16)" | |
| if grep -Fxq "$LOW_COVERAGE_DELIMITER" <<< "$LOW_COVERAGE_FILES"; then | |
| echo "Generated delimiter unexpectedly occurs in coverage data" | |
| exit 1 | |
| fi | |
| { | |
| echo "LOW_COVERAGE_FILES<<$LOW_COVERAGE_DELIMITER" | |
| echo "$LOW_COVERAGE_FILES" | |
| echo "$LOW_COVERAGE_DELIMITER" | |
| } >> "$GITHUB_ENV" | |
| echo "β Coverage data extracted successfully" | |
| else | |
| echo "β Could not find index.html in coverage report" | |
| exit 1 | |
| fi | |
| else | |
| echo "β Could not find coverage report artifact" | |
| echo "Available artifacts from the build:" | |
| echo "$ARTIFACTS_RESPONSE" | jq -r '.value[]?.name // "No artifacts found"' 2>/dev/null || echo "Could not parse artifacts list" | |
| echo "This indicates the Azure DevOps build may not have generated coverage reports" | |
| exit 1 | |
| fi | |
| - name: Download coverage XML from ADO | |
| run: | | |
| # Download the Cobertura XML directly from the CodeCoverageReport job | |
| BUILD_ID=${{ env.BUILD_ID }} | |
| ARTIFACTS_URL="https://dev.azure.com/SqlClientDrivers/public/_apis/build/builds/$BUILD_ID/artifacts?api-version=7.1-preview.5" | |
| echo "π₯ Fetching artifacts for build $BUILD_ID to find coverage files..." | |
| for i in {1..5}; do | |
| if ARTIFACTS_RESPONSE=$(curl --fail --silent --show-error --connect-timeout 10 --max-time 30 "$ARTIFACTS_URL") && | |
| jq -e 'type == "object" and (.value | type == "array")' <<< "$ARTIFACTS_RESPONSE" >/dev/null 2>&1; then | |
| break | |
| fi | |
| echo "β οΈ Artifacts API HTTP/JSON error ($i/5)" | |
| if [[ $i -eq 5 ]]; then | |
| echo "β Azure DevOps artifacts API unavailable after 5 attempts" | |
| exit 1 | |
| fi | |
| sleep 5 | |
| done | |
| echo "π Available artifacts:" | |
| echo "$ARTIFACTS_RESPONSE" | jq -r '.value[]?.name // "No artifacts found"' | |
| # Look for the unified coverage artifact from CodeCoverageReport job | |
| COVERAGE_XML_ARTIFACT=$(echo "$ARTIFACTS_RESPONSE" | jq -r '.value[]? | select(.name | test("unified-coverage|Code Coverage Report|coverage")) | .resource.downloadUrl // empty' 2>/dev/null | head -1) | |
| if [[ -n "$COVERAGE_XML_ARTIFACT" && "$COVERAGE_XML_ARTIFACT" != "null" && "$COVERAGE_XML_ARTIFACT" != "empty" ]]; then | |
| echo "π Downloading coverage artifact from: $COVERAGE_XML_ARTIFACT" | |
| COVERAGE_XML_ARCHIVE="$RUNNER_TEMP/coverage-artifacts.zip" | |
| if ! curl -L "$COVERAGE_XML_ARTIFACT" -o "$COVERAGE_XML_ARCHIVE" --fail --silent --show-error \ | |
| --connect-timeout 10 --max-time 60 --max-filesize 268435456 \ | |
| --retry 2 --retry-delay 5 --retry-max-time 180; then | |
| echo "β Failed to download coverage artifacts" | |
| exit 1 | |
| fi | |
| COVERAGE_XML="$RUNNER_TEMP/coverage.xml" | |
| if ! python .github/scripts/extract_coverage_artifact.py xml "$COVERAGE_XML_ARCHIVE" "$COVERAGE_XML"; then | |
| echo "β Failed to read the coverage XML artifact" | |
| exit 1 | |
| fi | |
| echo "β Coverage XML file is ready at $COVERAGE_XML" | |
| ls -la "$COVERAGE_XML" | |
| else | |
| echo "β Could not find coverage artifacts" | |
| echo "This indicates the Azure DevOps CodeCoverageReport job may not have run successfully" | |
| exit 1 | |
| fi | |
| - name: Generate patch coverage report | |
| env: | |
| COVERAGE_XML: ${{ runner.temp }}/coverage.xml | |
| run: | | |
| # Install dependencies | |
| pip install diff-cover jq | |
| sudo apt-get update && sudo apt-get install -y libxml2-utils | |
| # Verify coverage.xml exists before proceeding | |
| if [[ ! -f "$COVERAGE_XML" ]]; then | |
| echo "β coverage.xml not found in current directory" | |
| echo "Available files:" | |
| ls -la | head -20 | |
| exit 1 | |
| fi | |
| echo "β coverage.xml found, size: $(wc -c < "$COVERAGE_XML") bytes" | |
| echo "π Coverage file preview (first 10 lines):" | |
| head -10 "$COVERAGE_XML" | |
| # Generate diff coverage report using the new command format | |
| echo "π Generating patch coverage report..." | |
| # Debug: Show git status and branches before running diff-cover | |
| echo "π Git status before diff-cover:" | |
| git status --porcelain || echo "Git status failed" | |
| echo "Current branch: $(git branch --show-current)" | |
| echo "Available branches:" | |
| git branch -a | |
| echo "Checking if main branch is accessible:" | |
| git log --oneline -n 5 main || echo "Could not access main branch" | |
| # Debug: Show what diff-cover will analyze | |
| echo "π Git diff analysis:" | |
| echo "Files changed between main and current branch:" | |
| git diff --name-only main || echo "Could not get diff" | |
| echo "Detailed diff for Python files:" | |
| git diff main -- "*.py" | head -50 || echo "Could not get Python diff" | |
| # Debug: Check coverage.xml content for specific files | |
| echo "π Coverage.xml analysis:" | |
| echo "Python files mentioned in coverage.xml:" | |
| grep -o 'filename="[^"]*\.py"' "$COVERAGE_XML" | head -10 || echo "Could not extract filenames" | |
| echo "Sample coverage data:" | |
| head -20 "$COVERAGE_XML" | |
| # Use the new format for diff-cover commands | |
| echo "π Running diff-cover..." | |
| diff-cover "$COVERAGE_XML" \ | |
| --compare-branch=main \ | |
| --html-report patch-coverage.html \ | |
| --json-report patch-coverage.json \ | |
| --markdown-report patch-coverage.md || { | |
| echo "β diff-cover failed with exit code $?" | |
| echo "Checking if coverage.xml is valid XML..." | |
| if ! xmllint --noout "$COVERAGE_XML" 2>/dev/null; then | |
| echo "β coverage.xml is not valid XML" | |
| echo "First 50 lines of coverage.xml:" | |
| head -50 "$COVERAGE_XML" | |
| else | |
| echo "β coverage.xml is valid XML" | |
| echo "π diff-cover verbose output:" | |
| diff-cover "$COVERAGE_XML" --compare-branch=main --markdown-report debug-patch-coverage.md -v || echo "Verbose diff-cover also failed" | |
| fi | |
| # Don't exit here, let's see what files were created | |
| } | |
| # Check what files were generated | |
| echo "π Files generated after diff-cover:" | |
| ls -la patch-coverage.* || echo "No patch-coverage files found" | |
| ls -la *.md *.html *.json | grep -E "(patch|coverage)" || echo "No coverage-related files found" | |
| # Extract patch coverage percentage | |
| if [[ -f patch-coverage.json ]]; then | |
| echo "π Patch coverage analysis from JSON:" | |
| echo "Raw JSON content:" | |
| cat patch-coverage.json | jq . || echo "Could not parse JSON" | |
| PATCH_COVERAGE=$(jq -r '.total_percent_covered // "N/A"' patch-coverage.json) | |
| TOTAL_STATEMENTS=$(jq -r '.total_num_lines // "N/A"' patch-coverage.json) | |
| MISSING_STATEMENTS=$(jq -r '.total_num_missing // "N/A"' patch-coverage.json) | |
| if [[ ! "$PATCH_COVERAGE" =~ ^[0-9]+([.][0-9]+)?$ ]]; then | |
| PATCH_COVERAGE="N/A" | |
| fi | |
| echo "β Patch coverage: ${PATCH_COVERAGE}%" | |
| echo "π Total lines: $TOTAL_STATEMENTS, Missing: $MISSING_STATEMENTS" | |
| # Debug: Show per-file breakdown | |
| echo "π Per-file coverage breakdown:" | |
| jq -r '.src_stats // {} | to_entries[] | "\(.key): \(.value.percent_covered)% (\(.value.num_lines) lines, \(.value.num_missing) missing)"' patch-coverage.json || echo "Could not extract per-file stats" | |
| echo "PATCH_COVERAGE_PCT=${PATCH_COVERAGE}%" >> $GITHUB_ENV | |
| elif [[ -f patch-coverage.md ]]; then | |
| echo "π Extracting patch coverage from markdown file:" | |
| echo "Markdown content:" | |
| cat patch-coverage.md | |
| # Extract coverage percentage from markdown | |
| PATCH_COVERAGE=$(grep -o "Coverage.*[0-9]*%" patch-coverage.md | grep -o "[0-9]*%" | head -1 | sed 's/%//') | |
| TOTAL_LINES=$(grep -o "Total.*[0-9]* lines" patch-coverage.md | grep -o "[0-9]*" | head -1) | |
| MISSING_LINES=$(grep -o "Missing.*[0-9]* lines" patch-coverage.md | grep -o "[0-9]*" | tail -1) | |
| if [[ -n "$PATCH_COVERAGE" ]]; then | |
| echo "β Extracted patch coverage: ${PATCH_COVERAGE}%" | |
| echo "π Total lines: $TOTAL_LINES, Missing: $MISSING_LINES" | |
| echo "PATCH_COVERAGE_PCT=${PATCH_COVERAGE}%" >> $GITHUB_ENV | |
| else | |
| echo "β οΈ Could not extract coverage percentage from markdown" | |
| echo "PATCH_COVERAGE_PCT=Could not parse" >> $GITHUB_ENV | |
| fi | |
| else | |
| echo "β οΈ No patch coverage files generated" | |
| echo "π Checking for other output files:" | |
| ls -la *coverage* || echo "No coverage files found" | |
| echo "PATCH_COVERAGE_PCT=Report not generated" >> $GITHUB_ENV | |
| fi | |
| # Extract summary for comment | |
| if [[ -f patch-coverage.md ]]; then | |
| PATCH_COVERAGE_DELIMITER="coverage_$(openssl rand -hex 16)" | |
| if grep -Fxq "$PATCH_COVERAGE_DELIMITER" patch-coverage.md; then | |
| echo "Generated delimiter unexpectedly occurs in patch coverage data" | |
| exit 1 | |
| fi | |
| { | |
| echo "PATCH_COVERAGE_SUMMARY<<$PATCH_COVERAGE_DELIMITER" | |
| cat patch-coverage.md | |
| echo "$PATCH_COVERAGE_DELIMITER" | |
| } >> "$GITHUB_ENV" | |
| echo "β Patch coverage markdown summary ready" | |
| else | |
| echo "β οΈ patch-coverage.md not generated" | |
| echo "PATCH_COVERAGE_SUMMARY=Patch coverage report could not be generated." >> $GITHUB_ENV | |
| fi | |
| - name: Save coverage data for comment | |
| run: | | |
| mkdir -p coverage-comment-data | |
| jq -n \ | |
| --arg coverage_percentage "${{ env.COVERAGE_PERCENTAGE }}" \ | |
| --arg covered_lines "${{ env.COVERED_LINES }}" \ | |
| --arg total_lines "${{ env.TOTAL_LINES }}" \ | |
| --arg patch_coverage_pct "${{ env.PATCH_COVERAGE_PCT }}" \ | |
| --arg low_coverage_files "$LOW_COVERAGE_FILES" \ | |
| --arg ado_url "${{ env.ADO_URL }}" \ | |
| '{ | |
| coverage_percentage: $coverage_percentage, | |
| covered_lines: $covered_lines, | |
| total_lines: $total_lines, | |
| patch_coverage_pct: $patch_coverage_pct, | |
| low_coverage_files: $low_coverage_files, | |
| ado_url: $ado_url | |
| }' > coverage-comment-data/pr-info.json | |
| # Validate JSON before uploading | |
| echo "Validating generated JSON..." | |
| jq . coverage-comment-data/pr-info.json > /dev/null || { | |
| echo "β Invalid JSON generated" | |
| cat coverage-comment-data/pr-info.json | |
| exit 1 | |
| } | |
| echo "β JSON validation successful" | |
| cat coverage-comment-data/pr-info.json | |
| - name: Upload coverage comment data | |
| # Only upload artifact for forked PRs since same-repo PRs post comment directly | |
| # This prevents unnecessary workflow_run triggers for same-repo PRs | |
| if: github.event.pull_request.head.repo.full_name != github.repository | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: coverage-comment-data | |
| path: coverage-comment-data/ | |
| retention-days: 7 | |
| - name: Comment coverage summary on PR | |
| # Skip for forked PRs due to token permission restrictions | |
| if: github.event.pull_request.head.repo.full_name == github.repository | |
| uses: ./.github/actions/post-coverage-comment | |
| with: | |
| pr_number: ${{ github.event.pull_request.number }} | |
| coverage_percentage: ${{ env.COVERAGE_PERCENTAGE }} | |
| covered_lines: ${{ env.COVERED_LINES }} | |
| total_lines: ${{ env.TOTAL_LINES }} | |
| patch_coverage_pct: ${{ env.PATCH_COVERAGE_PCT }} | |
| low_coverage_files: ${{ env.LOW_COVERAGE_FILES }} | |
| patch_coverage_summary: ${{ env.PATCH_COVERAGE_SUMMARY }} | |
| ado_url: ${{ env.ADO_URL }} |