Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions .github/actions/post-coverage-comment/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Post Coverage Comment
description: Posts a standardized code coverage comment on a pull request

inputs:
pr_number:
description: 'Pull request number'
required: true
coverage_percentage:
description: 'Overall coverage percentage'
required: true
covered_lines:
description: 'Number of covered lines'
required: true
total_lines:
description: 'Total number of lines'
required: true
patch_coverage_pct:
description: 'Patch/diff coverage percentage'
required: true
low_coverage_files:
description: 'Files with lowest coverage (multiline)'
required: true
patch_coverage_summary:
description: 'Patch coverage summary markdown (multiline)'
required: true
ado_url:
description: 'Azure DevOps build URL'
required: true

runs:
using: composite
steps:
- name: Post coverage comment
uses: marocchino/sticky-pull-request-comment@v2
with:
header: Code Coverage Report
number: ${{ inputs.pr_number }}
message: |
# 📊 Code Coverage Report

<table>
<tr>
<td align="center" width="200">

### 🔥 Diff Coverage
### **${{ inputs.patch_coverage_pct }}**
<br>
</td>
<td align="center" width="200">

### 🎯 Overall Coverage
### **${{ inputs.coverage_percentage }}**
<br>
</td>
<td>

**📈 Total Lines Covered:** `${{ inputs.covered_lines }}` out of `${{ inputs.total_lines }}`
**📁 Project:** `mssql-python`

</td>
</tr>
</table>

---

${{ inputs.patch_coverage_summary }}

---
### 📋 Files Needing Attention

<details>
<summary>📉 <strong>Files with overall lowest coverage</strong> (click to expand)</summary>
<br>

```diff
${{ inputs.low_coverage_files }}
```

</details>

---
### 🔗 Quick Links

<table>
<tr>
<td align="left" width="200">
<b>⚙️ Build Summary</b>
</td>
<td align="left">
<b>📋 Coverage Details</b>
</td>
</tr>
<tr>
<td align="left" width="200">

[View Azure DevOps Build](${{ inputs.ado_url }})

</td>
<td align="left">

[Browse Full Coverage Report](${{ inputs.ado_url }}&view=codecoverage-tab)

</td>
</tr>
</table>
111 changes: 111 additions & 0 deletions .github/workflows/forked-pr-coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Post Coverage Comment

# This workflow handles posting coverage comments for FORKED PRs.
#
# Why a separate workflow?
# - Forked PRs have restricted GITHUB_TOKEN permissions for security
# - They cannot write comments directly to the base repository's PRs
# - workflow_run triggers run in the BASE repository context with full permissions
# - This allows us to safely post comments on forked PRs
#
# How it works:
# 1. PR Code Coverage workflow uploads coverage data as an artifact (forked PRs only)
# 2. This workflow triggers when PR Code Coverage completes successfully
# 3. Downloads the artifact and posts the comment with full write permissions
#
# Same-repo PRs post comments directly in pr-code-coverage.yml (faster)
# Forked PRs use this workflow (required for permissions)

on:
workflow_run:
workflows: ["PR Code Coverage"]
types:
- completed

jobs:
post-comment:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
permissions:
pull-requests: write
contents: read

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Download coverage data
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Download artifact with error handling for non-existent artifacts
if ! gh run download ${{ github.event.workflow_run.id }} \
--repo ${{ github.repository }} \
--name coverage-comment-data 2>&1; then
echo "⚠️ No coverage-comment-data artifact found"
echo "This is expected for same-repo PRs (they post comments directly)"
echo "Exiting gracefully..."
exit 0
fi

# Verify artifact was downloaded
if [[ ! -f pr-info.json ]]; then
echo "⚠️ Artifact downloaded but pr-info.json not found"
echo "This may indicate an issue with artifact upload"
exit 1
fi

- name: Read coverage data
id: coverage
run: |
if [[ ! -f pr-info.json ]]; then
echo "❌ pr-info.json not found"
exit 1
fi

cat pr-info.json

# Extract values from JSON with proper quoting
PR_NUMBER="$(jq -r '.pr_number' pr-info.json)"
COVERAGE_PCT="$(jq -r '.coverage_percentage' pr-info.json)"
COVERED_LINES="$(jq -r '.covered_lines' pr-info.json)"
TOTAL_LINES="$(jq -r '.total_lines' pr-info.json)"
PATCH_PCT="$(jq -r '.patch_coverage_pct' pr-info.json)"
LOW_COV_FILES="$(jq -r '.low_coverage_files' pr-info.json)"
PATCH_SUMMARY="$(jq -r '.patch_coverage_summary' pr-info.json)"
ADO_URL="$(jq -r '.ado_url' pr-info.json)"

# Export to env for next step (single-line values)
echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_ENV
echo "COVERAGE_PERCENTAGE=${COVERAGE_PCT}" >> $GITHUB_ENV
echo "COVERED_LINES=${COVERED_LINES}" >> $GITHUB_ENV
echo "TOTAL_LINES=${TOTAL_LINES}" >> $GITHUB_ENV
echo "PATCH_COVERAGE_PCT=${PATCH_PCT}" >> $GITHUB_ENV
echo "ADO_URL=${ADO_URL}" >> $GITHUB_ENV

# Handle multiline values with proper quoting
{
echo "LOW_COVERAGE_FILES<<EOF"
echo "$LOW_COV_FILES"
echo "EOF"
} >> $GITHUB_ENV

{
echo "PATCH_COVERAGE_SUMMARY<<EOF"
echo "$PATCH_SUMMARY"
echo "EOF"
} >> $GITHUB_ENV

- name: Comment coverage summary on PR
uses: ./.github/actions/post-coverage-comment
with:
pr_number: ${{ env.PR_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 }}
124 changes: 54 additions & 70 deletions .github/workflows/pr-code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -417,75 +417,59 @@ jobs:
echo "PATCH_COVERAGE_SUMMARY=Patch coverage report could not be generated." >> $GITHUB_ENV
fi
- name: Comment coverage summary on PR
uses: marocchino/sticky-pull-request-comment@v2
with:
header: Code Coverage Report
message: |
# 📊 Code Coverage Report
<table>
<tr>
<td align="center" width="200">
### 🔥 Diff Coverage
### **${{ env.PATCH_COVERAGE_PCT }}**
<br>
</td>
<td align="center" width="200">
### 🎯 Overall Coverage
### **${{ env.COVERAGE_PERCENTAGE }}**
<br>
</td>
<td>
**📈 Total Lines Covered:** `${{ env.COVERED_LINES }}` out of `${{ env.TOTAL_LINES }}`
**📁 Project:** `mssql-python`
</td>
</tr>
</table>
---
${{ env.PATCH_COVERAGE_SUMMARY }}
---
### 📋 Files Needing Attention
<details>
<summary>📉 <strong>Files with overall lowest coverage</strong> (click to expand)</summary>
<br>
```diff
${{ env.LOW_COVERAGE_FILES }}
```
</details>
---
### 🔗 Quick Links
<table>
<tr>
<td align="left" width="200">
<b>⚙️ Build Summary</b>
</td>
<td align="left">
<b>📋 Coverage Details</b>
</td>
</tr>
<tr>
<td align="left" width="200">
[View Azure DevOps Build](${{ env.ADO_URL }})
</td>
<td align="left">
- name: Save coverage data for comment
run: |
mkdir -p coverage-comment-data
jq -n \
--arg pr_number "${{ github.event.pull_request.number }}" \
--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 "${{ env.LOW_COVERAGE_FILES }}" \
--arg patch_coverage_summary "${{ env.PATCH_COVERAGE_SUMMARY }}" \
--arg ado_url "${{ env.ADO_URL }}" \
'{
pr_number: $pr_number,
coverage_percentage: $coverage_percentage,
covered_lines: $covered_lines,
total_lines: $total_lines,
patch_coverage_pct: $patch_coverage_pct,
low_coverage_files: $low_coverage_files,
patch_coverage_summary: $patch_coverage_summary,
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
[Browse Full Coverage Report](${{ env.ADO_URL }}&view=codecoverage-tab)
- 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@v4
with:
name: coverage-comment-data
path: coverage-comment-data/
retention-days: 7

</td>
</tr>
</table>
- 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 }}
Loading