Skip to content

Commit 9d93801

Browse files
authored
Merge pull request #33 from apuchmarcos/fixCI/coverageComment
fix(ci): use workflow_run for fork-safe PR coverage comments
2 parents fc4e2cb + 7806c35 commit 9d93801

File tree

2 files changed

+103
-30
lines changed

2 files changed

+103
-30
lines changed

.github/workflows/ci-cd.yml

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ jobs:
4545
build-and-test:
4646
name: Build and Test with Coverage
4747
runs-on: ubuntu-latest
48-
permissions:
49-
contents: read
50-
pull-requests: write # Required for posting comments on PRs
51-
5248
steps:
5349
- name: Checkout code
5450
uses: actions/checkout@v4
@@ -92,35 +88,22 @@ jobs:
9288
TOTAL=$(go tool cover -func=coverage.out | grep total | awk '{print $3}')
9389
echo "total=$TOTAL" >> $GITHUB_OUTPUT
9490
95-
# Publish coverage report as PR comment
96-
- name: Publish coverage comment on PR
91+
# Save coverage data and PR number for the coverage-comment workflow
92+
- name: Save coverage data for PR comment
9793
if: github.event_name == 'pull_request'
98-
env:
99-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100-
COVERAGE: ${{ steps.coverage.outputs.total }}
10194
run: |
102-
# Create comment body using heredoc
103-
cat << 'EOF' > comment.md
104-
## 📊 Coverage Report
105-
106-
**Total Coverage:** `${{ steps.coverage.outputs.total }}`
107-
108-
<details>
109-
<summary>📋 Coverage Details</summary>
95+
mkdir -p coverage-report
96+
cp coverage-summary.txt coverage-report/
97+
echo "${{ steps.coverage.outputs.total }}" > coverage-report/coverage-total.txt
98+
echo "${{ github.event.pull_request.number }}" > coverage-report/pr-number.txt
11099
111-
```
112-
EOF
113-
cat coverage-summary.txt >> comment.md
114-
cat << 'EOF' >> comment.md
115-
```
116-
117-
</details>
118-
119-
---
120-
*Generated by CI workflow*
121-
EOF
122-
123-
gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md
100+
- name: Upload coverage report artifact
101+
if: github.event_name == 'pull_request'
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: coverage-report
105+
path: coverage-report/
106+
retention-days: 1
124107

125108
# Upload binaries as artifacts (for all runs)
126109
- name: Upload binaries as artifacts
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# ============================================================================
2+
# Coverage Comment Workflow
3+
# ============================================================================
4+
# Posts coverage reports as PR comments using the workflow_run event.
5+
# This approach is fork-safe: the GITHUB_TOKEN has write access because
6+
# this workflow always runs from the default branch's code, not the fork's.
7+
#
8+
# Security:
9+
# - All artifact data (PR number, coverage values) is validated with
10+
# strict regex before use to prevent injection attacks.
11+
# - This workflow's code cannot be modified by fork PRs.
12+
# ============================================================================
13+
14+
name: Coverage Comment
15+
16+
on:
17+
workflow_run:
18+
workflows: ["CI/CD"]
19+
types:
20+
- completed
21+
22+
permissions:
23+
pull-requests: write
24+
25+
jobs:
26+
comment:
27+
name: Post Coverage Comment
28+
runs-on: ubuntu-latest
29+
# Only run when the triggering workflow was a successful PR build
30+
if: >
31+
github.event.workflow_run.event == 'pull_request' &&
32+
github.event.workflow_run.conclusion == 'success'
33+
34+
steps:
35+
- name: Download coverage artifact
36+
uses: actions/download-artifact@v4
37+
with:
38+
name: coverage-report
39+
path: coverage-report
40+
run-id: ${{ github.event.workflow_run.id }}
41+
github-token: ${{ secrets.GITHUB_TOKEN }}
42+
43+
# Validate and extract PR number (must be numeric only)
44+
- name: Validate and read PR number
45+
id: pr
46+
run: |
47+
PR_NUMBER=$(cat coverage-report/pr-number.txt | tr -d '[:space:]')
48+
if ! echo "$PR_NUMBER" | grep -qE '^[0-9]+$'; then
49+
echo "::error::Invalid PR number: '$PR_NUMBER'"
50+
exit 1
51+
fi
52+
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
53+
54+
# Validate and extract coverage total (must match percentage pattern)
55+
- name: Validate and read coverage total
56+
id: coverage
57+
run: |
58+
TOTAL=$(cat coverage-report/coverage-total.txt | tr -d '[:space:]')
59+
if ! echo "$TOTAL" | grep -qE '^[0-9]+(\.[0-9]+)?%$'; then
60+
echo "::error::Invalid coverage value: '$TOTAL'"
61+
exit 1
62+
fi
63+
echo "total=$TOTAL" >> $GITHUB_OUTPUT
64+
65+
# Build and post the PR comment
66+
- name: Post coverage comment
67+
env:
68+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69+
run: |
70+
{
71+
echo "## 📊 Coverage Report"
72+
echo ""
73+
echo "**Total Coverage:** \`${{ steps.coverage.outputs.total }}\`"
74+
echo ""
75+
echo "<details>"
76+
echo "<summary>📋 Coverage Details</summary>"
77+
echo ""
78+
echo '```'
79+
cat coverage-report/coverage-summary.txt
80+
echo '```'
81+
echo ""
82+
echo "</details>"
83+
echo ""
84+
echo "---"
85+
echo "*Generated by CI workflow*"
86+
} > comment.md
87+
88+
gh pr comment ${{ steps.pr.outputs.number }} \
89+
--repo "${{ github.repository }}" \
90+
--body-file comment.md

0 commit comments

Comments
 (0)