Skip to content

Commit b84f27e

Browse files
committed
extract codecov from pr-check
1 parent b58d436 commit b84f27e

2 files changed

Lines changed: 194 additions & 165 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
name: Codecov Upload
2+
3+
on:
4+
workflow_run:
5+
workflows: ["PR Check"]
6+
types: [completed]
7+
8+
jobs:
9+
coverage-gate:
10+
name: Coverage Gate
11+
if: github.event.workflow_run.conclusion == 'success'
12+
runs-on: ubuntu-latest
13+
14+
permissions:
15+
contents: read
16+
actions: read
17+
18+
defaults:
19+
run:
20+
shell: bash
21+
22+
steps:
23+
- name: Get PR info from triggering workflow
24+
id: pr-info
25+
uses: actions/github-script@v7
26+
with:
27+
script: |
28+
const run_id = context.payload.workflow_run.id;
29+
const prs = context.payload.workflow_run.pull_requests;
30+
if (!prs || prs.length === 0) {
31+
core.setFailed('No pull request associated with this workflow run.');
32+
return;
33+
}
34+
const pr = prs[0];
35+
core.setOutput('pr_number', pr.number);
36+
core.setOutput('head_sha', pr.head.sha);
37+
core.setOutput('head_ref', pr.head.ref);
38+
core.setOutput('base_ref', pr.base.ref);
39+
core.setOutput('run_id', run_id);
40+
41+
- name: Checkout code (needed by codecov-action for git context)
42+
uses: actions/checkout@v4
43+
with:
44+
ref: ${{ steps.pr-info.outputs.head_sha }}
45+
46+
- name: Download JaCoCo artifacts from PR Check
47+
uses: actions/download-artifact@v4
48+
with:
49+
name: jacoco-rockylinux
50+
path: artifacts/jacoco-rockylinux
51+
run-id: ${{ steps.pr-info.outputs.run_id }}
52+
github-token: ${{ secrets.GITHUB_TOKEN }}
53+
54+
- name: List downloaded reports
55+
run: |
56+
set -eux
57+
echo "JaCoCo XML reports found:"
58+
find artifacts/jacoco-rockylinux -name jacocoTestReport.xml
59+
60+
- name: Upload coverage to Codecov
61+
uses: codecov/codecov-action@v5
62+
with:
63+
token: ${{ secrets.CODECOV_TOKEN }}
64+
directory: artifacts/jacoco-rockylinux
65+
override_commit: ${{ steps.pr-info.outputs.head_sha }}
66+
override_branch: ${{ steps.pr-info.outputs.head_ref }}
67+
override_pr: ${{ steps.pr-info.outputs.pr_number }}
68+
verbose: true
69+
fail_ci_if_error: true
70+
71+
- name: Install tools
72+
run: sudo apt-get update && sudo apt-get install -y jq bc curl
73+
74+
- name: Wait for Codecov processing
75+
env:
76+
CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }}
77+
CODECOV_OWNER: ${{ github.repository_owner }}
78+
CODECOV_REPO: ${{ github.event.repository.name }}
79+
COMMIT_ID: ${{ steps.pr-info.outputs.head_sha }}
80+
run: |
81+
set -euxo pipefail
82+
83+
API_URL="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}/commits/${COMMIT_ID}"
84+
MAX_ATTEMPTS=20
85+
INTERVAL=30
86+
87+
for i in $(seq 1 $MAX_ATTEMPTS); do
88+
echo "=== Polling attempt $i / $MAX_ATTEMPTS ==="
89+
90+
http_code=$(curl -sS -o /tmp/poll.json -w '%{http_code}' \
91+
-H "Authorization: Bearer ${CODECOV_API_TOKEN}" \
92+
"$API_URL")
93+
94+
if [ "$http_code" = "200" ]; then
95+
state=$(jq -r '.state // "unknown"' /tmp/poll.json)
96+
echo "Commit processing state: $state"
97+
if [ "$state" = "complete" ]; then
98+
echo "Codecov has finished processing."
99+
exit 0
100+
fi
101+
else
102+
echo "HTTP $http_code — commit not yet available."
103+
cat /tmp/poll.json 2>/dev/null || true
104+
fi
105+
106+
if [ "$i" -lt "$MAX_ATTEMPTS" ]; then
107+
sleep "$INTERVAL"
108+
fi
109+
done
110+
111+
echo "Timed out waiting for Codecov (${MAX_ATTEMPTS} x ${INTERVAL}s)."
112+
exit 1
113+
114+
- name: Coverage gate via Codecov REST API
115+
env:
116+
CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }}
117+
CODECOV_OWNER: ${{ github.repository_owner }}
118+
CODECOV_REPO: ${{ github.event.repository.name }}
119+
COMMIT_ID: ${{ steps.pr-info.outputs.head_sha }}
120+
BASE_BRANCH: ${{ steps.pr-info.outputs.base_ref }}
121+
PR_NUMBER: ${{ steps.pr-info.outputs.pr_number }}
122+
run: |
123+
set -euxo pipefail
124+
125+
API_BASE="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}"
126+
AUTH="Authorization: Bearer ${CODECOV_API_TOKEN}"
127+
128+
# Helper: GET with error handling
129+
api_get() {
130+
local url="$1"
131+
local http_code
132+
http_code=$(curl -sS -o /tmp/api_out.json -w '%{http_code}' \
133+
-H "$AUTH" "$url")
134+
if [ "$http_code" != "200" ]; then
135+
echo "ERROR: GET $url => HTTP $http_code" >&2
136+
cat /tmp/api_out.json >&2
137+
return 1
138+
fi
139+
cat /tmp/api_out.json
140+
}
141+
142+
# 1) Current commit coverage
143+
echo "=== 1. Current commit coverage (sha: ${COMMIT_ID}) ==="
144+
commit_resp=$(api_get "${API_BASE}/totals/?sha=${COMMIT_ID}")
145+
self_cov=$(echo "$commit_resp" | jq -r '.totals.coverage // 0')
146+
echo "self_cov = ${self_cov}%"
147+
148+
# 2) Base branch head coverage
149+
echo "=== 2. Base branch coverage (branch: ${BASE_BRANCH}) ==="
150+
base_resp=$(api_get "${API_BASE}/totals/?branch=${BASE_BRANCH}")
151+
base_branch_cov=$(echo "$base_resp" | jq -r '.totals.coverage // 0')
152+
echo "base_branch_cov = ${base_branch_cov}%"
153+
154+
# 3) PR comparison — patch coverage
155+
echo "=== 3. PR #${PR_NUMBER} comparison ==="
156+
compare_resp=$(api_get "${API_BASE}/compare/?pullid=${PR_NUMBER}")
157+
patch_cov=$(echo "$compare_resp" | jq -r '.totals.patch.coverage // 0')
158+
impacted_files=$(echo "$compare_resp" | jq -r '(.files // []) | length')
159+
echo "patch_cov = ${patch_cov}%"
160+
echo "impacted_files = ${impacted_files}"
161+
162+
# ===== Gate Rules =====
163+
164+
# Rule 1: current commit must have valid coverage
165+
if [ "$(echo "$self_cov <= 0" | bc)" -eq 1 ]; then
166+
echo "FAIL: Could not retrieve valid coverage for commit ${COMMIT_ID}."
167+
exit 1
168+
fi
169+
170+
# Rule 2: overall coverage must not decrease vs base branch
171+
if [ "$(echo "$self_cov < $base_branch_cov" | bc)" -eq 1 ]; then
172+
echo "FAIL: Overall coverage decreased!"
173+
echo " Current commit : ${self_cov}%"
174+
echo " Base branch : ${base_branch_cov}%"
175+
echo "Please add unit tests to maintain coverage."
176+
exit 1
177+
fi
178+
179+
# Rule 3: patch coverage on changed files >= 80%
180+
# if [ "$impacted_files" -gt 0 ] && [ "$(echo "$patch_cov > 0" | bc)" -eq 1 ]; then
181+
# if [ "$(echo "$patch_cov < 80" | bc)" -eq 1 ]; then
182+
# echo "FAIL: Patch coverage is ${patch_cov}% (minimum 80%)."
183+
# echo "Please add tests for new/changed code."
184+
# exit 1
185+
# fi
186+
# else
187+
# echo "No impacted files or no patch data; skipping patch coverage check."
188+
# fi
189+
190+
echo ""
191+
echo "All coverage gates passed!"
192+
echo " Current commit : ${self_cov}%"
193+
echo " Base branch : ${base_branch_cov}%"
194+
echo " Patch coverage : ${patch_cov}%"

.github/workflows/pr-check.yml

Lines changed: 0 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -282,168 +282,3 @@ jobs:
282282

283283
- name: Build
284284
run: ./gradlew clean build --no-daemon --no-build-cache
285-
286-
287-
coverage-gate:
288-
name: Coverage Gate (from rockylinux build)
289-
needs: docker-build-rockylinux
290-
runs-on: ubuntu-latest
291-
292-
permissions:
293-
contents: read
294-
295-
defaults:
296-
run:
297-
shell: bash
298-
299-
steps:
300-
- name: Checkout code (needed by codecov-action for git context)
301-
uses: actions/checkout@v4
302-
303-
- name: Download JaCoCo artifacts (rockylinux)
304-
uses: actions/download-artifact@v4
305-
with:
306-
name: jacoco-rockylinux
307-
path: artifacts/jacoco-rockylinux
308-
309-
- name: List downloaded reports
310-
run: |
311-
set -eux
312-
echo "JaCoCo XML reports found:"
313-
find artifacts/jacoco-rockylinux -name jacocoTestReport.xml
314-
315-
- name: Upload coverage to Codecov
316-
uses: codecov/codecov-action@v5
317-
with:
318-
token: ${{ secrets.CODECOV_TOKEN }}
319-
directory: artifacts/jacoco-rockylinux
320-
override_commit: ${{ github.event.pull_request.head.sha }}
321-
override_branch: ${{ github.event.pull_request.head.ref }}
322-
override_pr: ${{ github.event.pull_request.number }}
323-
verbose: true
324-
fail_ci_if_error: true
325-
326-
- name: Install tools
327-
run: sudo apt-get update && sudo apt-get install -y jq bc curl
328-
329-
- name: Wait for Codecov processing
330-
env:
331-
CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }}
332-
CODECOV_OWNER: ${{ github.repository_owner }}
333-
CODECOV_REPO: ${{ github.event.repository.name }}
334-
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
335-
run: |
336-
set -euxo pipefail
337-
338-
API_URL="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}/commits/${COMMIT_ID}"
339-
MAX_ATTEMPTS=20
340-
INTERVAL=30
341-
342-
for i in $(seq 1 $MAX_ATTEMPTS); do
343-
echo "=== Polling attempt $i / $MAX_ATTEMPTS ==="
344-
345-
http_code=$(curl -sS -o /tmp/poll.json -w '%{http_code}' \
346-
-H "Authorization: Bearer ${CODECOV_API_TOKEN}" \
347-
"$API_URL")
348-
349-
if [ "$http_code" = "200" ]; then
350-
state=$(jq -r '.state // "unknown"' /tmp/poll.json)
351-
echo "Commit processing state: $state"
352-
if [ "$state" = "complete" ]; then
353-
echo "Codecov has finished processing."
354-
exit 0
355-
fi
356-
else
357-
echo "HTTP $http_code — commit not yet available."
358-
cat /tmp/poll.json 2>/dev/null || true
359-
fi
360-
361-
if [ "$i" -lt "$MAX_ATTEMPTS" ]; then
362-
sleep "$INTERVAL"
363-
fi
364-
done
365-
366-
echo "Timed out waiting for Codecov (${MAX_ATTEMPTS} x ${INTERVAL}s)."
367-
exit 1
368-
369-
- name: Coverage gate via Codecov REST API
370-
env:
371-
CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }}
372-
CODECOV_OWNER: ${{ github.repository_owner }}
373-
CODECOV_REPO: ${{ github.event.repository.name }}
374-
COMMIT_ID: ${{ github.event.pull_request.head.sha }}
375-
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
376-
PR_NUMBER: ${{ github.event.pull_request.number }}
377-
run: |
378-
set -euxo pipefail
379-
380-
API_BASE="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}"
381-
AUTH="Authorization: Bearer ${CODECOV_API_TOKEN}"
382-
383-
# Helper: GET with error handling
384-
api_get() {
385-
local url="$1"
386-
local http_code
387-
http_code=$(curl -sS -o /tmp/api_out.json -w '%{http_code}' \
388-
-H "$AUTH" "$url")
389-
if [ "$http_code" != "200" ]; then
390-
echo "ERROR: GET $url => HTTP $http_code" >&2
391-
cat /tmp/api_out.json >&2
392-
return 1
393-
fi
394-
cat /tmp/api_out.json
395-
}
396-
397-
# 1) Current commit coverage
398-
echo "=== 1. Current commit coverage (sha: ${COMMIT_ID}) ==="
399-
commit_resp=$(api_get "${API_BASE}/totals/?sha=${COMMIT_ID}")
400-
self_cov=$(echo "$commit_resp" | jq -r '.totals.coverage // 0')
401-
echo "self_cov = ${self_cov}%"
402-
403-
# 2) Base branch head coverage
404-
echo "=== 2. Base branch coverage (branch: ${BASE_BRANCH}) ==="
405-
base_resp=$(api_get "${API_BASE}/totals/?branch=${BASE_BRANCH}")
406-
base_branch_cov=$(echo "$base_resp" | jq -r '.totals.coverage // 0')
407-
echo "base_branch_cov = ${base_branch_cov}%"
408-
409-
# 3) PR comparison — patch coverage
410-
echo "=== 3. PR #${PR_NUMBER} comparison ==="
411-
compare_resp=$(api_get "${API_BASE}/compare/?pullid=${PR_NUMBER}")
412-
patch_cov=$(echo "$compare_resp" | jq -r '.totals.patch.coverage // 0')
413-
impacted_files=$(echo "$compare_resp" | jq -r '(.files // []) | length')
414-
echo "patch_cov = ${patch_cov}%"
415-
echo "impacted_files = ${impacted_files}"
416-
417-
# ===== Gate Rules =====
418-
419-
# Rule 1: current commit must have valid coverage
420-
if [ "$(echo "$self_cov <= 0" | bc)" -eq 1 ]; then
421-
echo "FAIL: Could not retrieve valid coverage for commit ${COMMIT_ID}."
422-
exit 1
423-
fi
424-
425-
# Rule 2: overall coverage must not decrease vs base branch
426-
if [ "$(echo "$self_cov < $base_branch_cov" | bc)" -eq 1 ]; then
427-
echo "FAIL: Overall coverage decreased!"
428-
echo " Current commit : ${self_cov}%"
429-
echo " Base branch : ${base_branch_cov}%"
430-
echo "Please add unit tests to maintain coverage."
431-
exit 1
432-
fi
433-
434-
# Rule 3: patch coverage on changed files >= 80%
435-
# if [ "$impacted_files" -gt 0 ] && [ "$(echo "$patch_cov > 0" | bc)" -eq 1 ]; then
436-
# if [ "$(echo "$patch_cov < 80" | bc)" -eq 1 ]; then
437-
# echo "FAIL: Patch coverage is ${patch_cov}% (minimum 80%)."
438-
# echo "Please add tests for new/changed code."
439-
# exit 1
440-
# fi
441-
# else
442-
# echo "No impacted files or no patch data; skipping patch coverage check."
443-
# fi
444-
445-
echo ""
446-
echo "All coverage gates passed!"
447-
echo " Current commit : ${self_cov}%"
448-
echo " Base branch : ${base_branch_cov}%"
449-
echo " Patch coverage : ${patch_cov}%"

0 commit comments

Comments
 (0)