Skip to content

Commit a066b6b

Browse files
committed
Updated posting of coverage comments to replace existing comments.
1 parent 6b65fe0 commit a066b6b

7 files changed

Lines changed: 208 additions & 18 deletions

File tree

.circleci/config.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -410,18 +410,7 @@ jobs:
410410
command: |
411411
[ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0
412412
[ "${VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP:-0}" = "1" ] && exit 0
413-
[ -z "${CIRCLE_PULL_REQUEST}" ] && exit 0
414-
[ -z "${GITHUB_TOKEN}" ] && exit 0
415-
COVERAGE_CONTENT=$(sed '/./,$!d' /tmp/artifacts/coverage/phpunit/coverage.txt)
416-
PR_NUMBER=$(echo "${CIRCLE_PULL_REQUEST}" | cut -d'/' -f 7)
417-
REPO_SLUG="${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}"
418-
curl -s -X POST \
419-
-H "Authorization: token ${GITHUB_TOKEN}" \
420-
-H "Accept: application/vnd.github.v3+json" \
421-
"https://api.github.com/repos/${REPO_SLUG}/issues/${PR_NUMBER}/comments" \
422-
-d "$(jq -n --arg body "\`\`\`
423-
${COVERAGE_CONTENT}
424-
\`\`\`" '{body: $body}')"
413+
.circleci/post-coverage-comment.sh /tmp/artifacts/coverage/phpunit/coverage.txt
425414
426415
- run:
427416
name: Upload code coverage reports to Codecov

.circleci/post-coverage-comment.sh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
##
3+
## Post code coverage summary as a PR comment on GitHub.
4+
##
5+
## Minimizes previous coverage comments before posting a new one.
6+
##
7+
## Environment variables:
8+
## CIRCLE_PULL_REQUEST - CircleCI PR URL.
9+
## GITHUB_TOKEN - GitHub token for API access.
10+
## CIRCLE_PROJECT_USERNAME - GitHub org/user.
11+
## CIRCLE_PROJECT_REPONAME - GitHub repo name.
12+
##
13+
## Usage:
14+
## .circleci/post-coverage-comment.sh /path/to/coverage.txt
15+
16+
set -euo pipefail
17+
18+
COVERAGE_FILE="${1:-}"
19+
20+
if [ -z "${COVERAGE_FILE}" ] || [ ! -f "${COVERAGE_FILE}" ]; then
21+
echo "ERROR: Coverage file not found: ${COVERAGE_FILE}" >&2
22+
exit 1
23+
fi
24+
25+
if [ -z "${CIRCLE_PULL_REQUEST:-}" ]; then
26+
echo "Not a pull request. Skipping."
27+
exit 0
28+
fi
29+
30+
if [ -z "${GITHUB_TOKEN:-}" ]; then
31+
echo "GITHUB_TOKEN is not set. Skipping."
32+
exit 0
33+
fi
34+
35+
COVERAGE_CONTENT=$(sed '/./,$!d' "${COVERAGE_FILE}")
36+
PR_NUMBER=$(echo "${CIRCLE_PULL_REQUEST}" | cut -d'/' -f 7)
37+
REPO_SLUG="${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}"
38+
39+
MARKER="<!-- coverage-circleci -->"
40+
41+
BODY="$(jq -n --arg body "**Code coverage (CircleCI)**
42+
\`\`\`
43+
${COVERAGE_CONTENT}
44+
\`\`\`
45+
${MARKER}" '{body: $body}')"
46+
47+
# Minimize previous coverage comments.
48+
EXISTING_IDS=$(curl -s \
49+
-H "Authorization: token ${GITHUB_TOKEN}" \
50+
-H "Accept: application/vnd.github.v3+json" \
51+
"https://api.github.com/repos/${REPO_SLUG}/issues/${PR_NUMBER}/comments?per_page=100" |
52+
jq -r '.[] | select(.body | contains("<!-- coverage-circleci -->")) | .node_id')
53+
54+
for NODE_ID in ${EXISTING_IDS}; do
55+
curl -s -X POST \
56+
-H "Authorization: bearer ${GITHUB_TOKEN}" \
57+
-H "Content-Type: application/json" \
58+
"https://api.github.com/graphql" \
59+
-d "$(jq -n --arg id "${NODE_ID}" '{query: "mutation($id:ID!){minimizeComment(input:{subjectId:$id,classifier:OUTDATED}){minimizedComment{isMinimized}}}", variables: {id: $id}}')"
60+
done
61+
62+
# Post new coverage comment.
63+
curl -s -X POST \
64+
-H "Authorization: token ${GITHUB_TOKEN}" \
65+
-H "Accept: application/vnd.github.v3+json" \
66+
"https://api.github.com/repos/${REPO_SLUG}/issues/${PR_NUMBER}/comments" \
67+
-d "${BODY}"

.github/workflows/build-test-deploy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ jobs:
442442
if: ${{ github.event_name == 'pull_request' && (matrix.instance == 0 || strategy.job-total == 1) && vars.VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP != '1' }}
443443
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
444444
with:
445+
header: coverage-gha
445446
message: |
447+
**Code coverage (GitHub Actions)**
446448
```
447449
${{ env.COVERAGE_CONTENT }}
448450
```

.vortex/docs/content/continuous-integration/circleci.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,5 +224,6 @@ environment variable in **Project Settings → Environment Variables**. Default
224224
is `90` (percent).
225225

226226
Coverage reports can be posted as PR comments. This requires a `GITHUB_TOKEN`
227-
environment variable with permission to post comments. To disable PR comments,
228-
set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1`.
227+
environment variable with permission to post comments. Each new report replaces
228+
the previous one — older comments are minimized to keep the PR timeline clean.
229+
To disable PR comments, set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1`.

.vortex/docs/content/continuous-integration/github-actions.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,7 @@ Configure the threshold by setting the `VORTEX_CI_CODE_COVERAGE_THRESHOLD`
192192
variable in **Settings → Secrets and variables → Actions → Variables**. Default
193193
is `90` (percent).
194194

195-
Coverage reports are automatically posted as PR comments. To disable this, set
196-
`VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1`.
195+
Coverage reports are automatically posted as PR comments. Each new report
196+
replaces the previous one — older comments are minimized to keep the PR
197+
timeline clean. To disable this, set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP`
198+
to `1`.

.vortex/docs/content/tools/phpunit.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,12 @@ minimum percentage (default: `90`).
153153

154154
### PR comments
155155

156-
Coverage reports are posted as PR comments automatically. Set
157-
`VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1` to disable.
156+
Coverage reports are posted as PR comments automatically. Each new report
157+
replaces the previous one — older comments are minimized to keep the PR
158+
timeline clean. The comment includes a header indicating the CI source
159+
(GitHub Actions or CircleCI).
160+
161+
Set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1` to disable.
158162

159163
### Ignoring lines from coverage
160164

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env bats
2+
##
3+
# Unit tests for .circleci/post-coverage-comment.sh
4+
#
5+
# shellcheck disable=SC2030,SC2031,SC2034
6+
7+
load ../_helper.bash
8+
9+
@test "post-coverage-comment: missing coverage file" {
10+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
11+
12+
export CIRCLE_PULL_REQUEST="https://github.com/myorg/myrepo/pull/123"
13+
export GITHUB_TOKEN="token12345"
14+
export CIRCLE_PROJECT_USERNAME="myorg"
15+
export CIRCLE_PROJECT_REPONAME="myrepo"
16+
17+
run .circleci/post-coverage-comment.sh /nonexistent/file.txt
18+
assert_failure
19+
assert_output_contains "ERROR: Coverage file not found"
20+
21+
popd >/dev/null || exit 1
22+
}
23+
24+
@test "post-coverage-comment: no arguments" {
25+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
26+
27+
export CIRCLE_PULL_REQUEST="https://github.com/myorg/myrepo/pull/123"
28+
export GITHUB_TOKEN="token12345"
29+
30+
run .circleci/post-coverage-comment.sh
31+
assert_failure
32+
assert_output_contains "ERROR: Coverage file not found"
33+
34+
popd >/dev/null || exit 1
35+
}
36+
37+
@test "post-coverage-comment: skip when not a pull request" {
38+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
39+
40+
mkdir -p .logs/coverage/phpunit
41+
echo "Lines: 100.00%" >.logs/coverage/phpunit/coverage.txt
42+
43+
unset CIRCLE_PULL_REQUEST
44+
export GITHUB_TOKEN="token12345"
45+
46+
run .circleci/post-coverage-comment.sh .logs/coverage/phpunit/coverage.txt
47+
assert_success
48+
assert_output_contains "Not a pull request. Skipping."
49+
50+
popd >/dev/null || exit 1
51+
}
52+
53+
@test "post-coverage-comment: skip when no GITHUB_TOKEN" {
54+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
55+
56+
mkdir -p .logs/coverage/phpunit
57+
echo "Lines: 100.00%" >.logs/coverage/phpunit/coverage.txt
58+
59+
export CIRCLE_PULL_REQUEST="https://github.com/myorg/myrepo/pull/123"
60+
unset GITHUB_TOKEN
61+
62+
run .circleci/post-coverage-comment.sh .logs/coverage/phpunit/coverage.txt
63+
assert_success
64+
assert_output_contains "GITHUB_TOKEN is not set. Skipping."
65+
66+
popd >/dev/null || exit 1
67+
}
68+
69+
@test "post-coverage-comment: post with no existing comments" {
70+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
71+
72+
mkdir -p .logs/coverage/phpunit
73+
printf "Code Coverage Report:\n Lines: 100.00%%\n" >.logs/coverage/phpunit/coverage.txt
74+
75+
declare -a STEPS=(
76+
# GET existing comments - return empty array.
77+
"@curl -s -H Authorization: token token12345 -H Accept: application/vnd.github.v3+json https://api.github.com/repos/myorg/myrepo/issues/123/comments?per_page=100 # []"
78+
# POST new comment.
79+
'@curl -s -X POST -H Authorization: token token12345 -H Accept: application/vnd.github.v3+json https://api.github.com/repos/myorg/myrepo/issues/123/comments * # {"id": 1}'
80+
)
81+
82+
mocks="$(run_steps "setup")"
83+
84+
export CIRCLE_PULL_REQUEST="https://github.com/myorg/myrepo/pull/123"
85+
export GITHUB_TOKEN="token12345"
86+
export CIRCLE_PROJECT_USERNAME="myorg"
87+
export CIRCLE_PROJECT_REPONAME="myrepo"
88+
89+
run .circleci/post-coverage-comment.sh .logs/coverage/phpunit/coverage.txt
90+
assert_success
91+
92+
run_steps "assert" "${mocks[@]}"
93+
94+
popd >/dev/null || exit 1
95+
}
96+
97+
@test "post-coverage-comment: minimize existing comments before posting" {
98+
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1
99+
100+
mkdir -p .logs/coverage/phpunit
101+
printf "Code Coverage Report:\n Lines: 95.00%%\n" >.logs/coverage/phpunit/coverage.txt
102+
103+
declare -a STEPS=(
104+
# GET existing comments - return one with marker.
105+
'@curl -s -H Authorization: token token12345 -H Accept: application/vnd.github.v3+json https://api.github.com/repos/myorg/myrepo/issues/456/comments?per_page=100 # [{"node_id": "MDEyOklzc3VlQ29tbWVudDE=", "body": "old coverage <!-- coverage-circleci -->"}]'
106+
# POST GraphQL to minimize existing comment.
107+
'@curl -s -X POST -H Authorization: bearer token12345 -H Content-Type: application/json https://api.github.com/graphql * # {"data":{"minimizeComment":{"minimizedComment":{"isMinimized":true}}}}'
108+
# POST new comment.
109+
'@curl -s -X POST -H Authorization: token token12345 -H Accept: application/vnd.github.v3+json https://api.github.com/repos/myorg/myrepo/issues/456/comments * # {"id": 2}'
110+
)
111+
112+
mocks="$(run_steps "setup")"
113+
114+
export CIRCLE_PULL_REQUEST="https://github.com/myorg/myrepo/pull/456"
115+
export GITHUB_TOKEN="token12345"
116+
export CIRCLE_PROJECT_USERNAME="myorg"
117+
export CIRCLE_PROJECT_REPONAME="myrepo"
118+
119+
run .circleci/post-coverage-comment.sh .logs/coverage/phpunit/coverage.txt
120+
assert_success
121+
122+
run_steps "assert" "${mocks[@]}"
123+
124+
popd >/dev/null || exit 1
125+
}

0 commit comments

Comments
 (0)