Skip to content

Commit 437cb50

Browse files
bladehan1halibobo1205
authored andcommitted
feat(ci): add coverage check workflow and auto reviewer assignment (tronprotocol#6635)
1 parent 21dbd62 commit 437cb50

6 files changed

Lines changed: 382 additions & 107 deletions

File tree

.github/CODEOWNERS

Lines changed: 0 additions & 96 deletions
This file was deleted.

.github/workflows/pr-build.yml

Lines changed: 231 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ jobs:
2929
fail-fast: false
3030
matrix:
3131
include:
32-
- java: '8'
33-
runner: macos-26-intel
34-
arch: x86_64
3532
- java: '17'
3633
runner: macos-26
3734
arch: aarch64
@@ -57,10 +54,6 @@ jobs:
5754
- name: Build
5855
run: ./gradlew clean build --no-daemon
5956

60-
- name: Test with RocksDB engine
61-
if: matrix.arch == 'x86_64'
62-
run: ./gradlew :framework:testWithRocksDb --no-daemon
63-
6457
build-ubuntu:
6558
name: Build ubuntu24 (JDK 17 / aarch64)
6659
if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'ubuntu' }}
@@ -177,7 +170,236 @@ jobs:
177170
debian11-x86_64-gradle-
178171
179172
- name: Build
180-
run: ./gradlew clean build --no-daemon
173+
run: ./gradlew clean build --no-daemon --no-build-cache
181174

182175
- name: Test with RocksDB engine
183-
run: ./gradlew :framework:testWithRocksDb --no-daemon
176+
run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache
177+
178+
- name: Generate module coverage reports
179+
run: ./gradlew jacocoTestReport --no-daemon
180+
181+
- name: Upload PR coverage reports
182+
uses: actions/upload-artifact@v6
183+
with:
184+
name: jacoco-coverage-pr
185+
path: |
186+
**/build/reports/jacoco/test/jacocoTestReport.xml
187+
if-no-files-found: error
188+
189+
coverage-base:
190+
name: Coverage Base (JDK 8 / x86_64)
191+
if: ${{ github.event_name == 'pull_request' }}
192+
runs-on: ubuntu-latest
193+
timeout-minutes: 60
194+
container:
195+
image: eclipse-temurin:8-jdk # base image is Debian 11 (Bullseye)
196+
defaults:
197+
run:
198+
shell: bash
199+
env:
200+
GRADLE_USER_HOME: /github/home/.gradle
201+
permissions:
202+
contents: read
203+
204+
steps:
205+
- name: Checkout code
206+
uses: actions/checkout@v5
207+
with:
208+
ref: ${{ github.event.pull_request.base.sha }}
209+
210+
- name: Install dependencies (Debian + build tools)
211+
run: |
212+
set -euxo pipefail
213+
apt-get update
214+
apt-get install -y git wget unzip build-essential curl jq
215+
216+
- name: Cache Gradle packages
217+
uses: actions/cache@v4
218+
with:
219+
path: |
220+
/github/home/.gradle/caches
221+
/github/home/.gradle/wrapper
222+
key: coverage-base-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
223+
restore-keys: |
224+
coverage-base-x86_64-gradle-
225+
226+
- name: Build (base)
227+
run: ./gradlew clean build --no-daemon --no-build-cache
228+
229+
- name: Test with RocksDB engine (base)
230+
run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache
231+
232+
- name: Generate module coverage reports (base)
233+
run: ./gradlew jacocoTestReport --no-daemon
234+
235+
- name: Upload base coverage reports
236+
uses: actions/upload-artifact@v6
237+
with:
238+
name: jacoco-coverage-base
239+
path: |
240+
**/build/reports/jacoco/test/jacocoTestReport.xml
241+
if-no-files-found: error
242+
243+
coverage-gate:
244+
name: Coverage Gate
245+
needs: [docker-build-debian11, coverage-base]
246+
if: ${{ github.event_name == 'pull_request' }}
247+
runs-on: ubuntu-latest
248+
timeout-minutes: 10
249+
permissions:
250+
contents: read
251+
252+
steps:
253+
- name: Checkout code
254+
uses: actions/checkout@v5
255+
with:
256+
fetch-depth: 0
257+
258+
- name: Download base coverage reports
259+
uses: actions/download-artifact@v8
260+
with:
261+
name: jacoco-coverage-base
262+
path: coverage/base
263+
264+
- name: Download PR coverage reports
265+
uses: actions/download-artifact@v8
266+
with:
267+
name: jacoco-coverage-pr
268+
path: coverage/pr
269+
270+
- name: Collect coverage report paths
271+
id: collect-xml
272+
run: |
273+
BASE_XMLS=$(find coverage/base -name "jacocoTestReport.xml" | sort | paste -sd, -)
274+
PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort | paste -sd, -)
275+
if [ -z "$BASE_XMLS" ] || [ -z "$PR_XMLS" ]; then
276+
echo "Missing jacocoTestReport.xml files for base or PR."
277+
exit 1
278+
fi
279+
echo "base_xmls=$BASE_XMLS" >> "$GITHUB_OUTPUT"
280+
echo "pr_xmls=$PR_XMLS" >> "$GITHUB_OUTPUT"
281+
282+
- name: Aggregate base coverage
283+
id: jacoco-base
284+
uses: madrapps/jacoco-report@v1.7.2
285+
with:
286+
paths: ${{ steps.collect-xml.outputs.base_xmls }}
287+
token: ${{ secrets.GITHUB_TOKEN }}
288+
min-coverage-overall: 0
289+
min-coverage-changed-files: 0
290+
skip-if-no-changes: true
291+
title: '## Base Coverage Snapshot'
292+
update-comment: false
293+
294+
- name: Aggregate PR coverage
295+
id: jacoco-pr
296+
uses: madrapps/jacoco-report@v1.7.2
297+
with:
298+
paths: ${{ steps.collect-xml.outputs.pr_xmls }}
299+
token: ${{ secrets.GITHUB_TOKEN }}
300+
min-coverage-overall: 0
301+
min-coverage-changed-files: 0
302+
skip-if-no-changes: true
303+
title: '## PR Code Coverage Report'
304+
update-comment: false
305+
306+
- name: Enforce coverage gates
307+
env:
308+
BASE_OVERALL_RAW: ${{ steps.jacoco-base.outputs.coverage-overall }}
309+
PR_OVERALL_RAW: ${{ steps.jacoco-pr.outputs.coverage-overall }}
310+
PR_CHANGED_RAW: ${{ steps.jacoco-pr.outputs.coverage-changed-files }}
311+
run: |
312+
set -euo pipefail
313+
314+
MIN_CHANGED=60
315+
MAX_DROP=-0.1
316+
317+
sanitize() {
318+
echo "$1" | tr -d ' %'
319+
}
320+
is_number() {
321+
[[ "$1" =~ ^-?[0-9]+([.][0-9]+)?$ ]]
322+
}
323+
compare_float() {
324+
# Usage: compare_float "<expr>"
325+
# Example: compare_float "1.2 >= -0.1"
326+
awk "BEGIN { if ($1) print 1; else print 0 }"
327+
}
328+
329+
# 1) Parse metrics from jacoco-report outputs
330+
BASE_OVERALL="$(sanitize "$BASE_OVERALL_RAW")"
331+
PR_OVERALL="$(sanitize "$PR_OVERALL_RAW")"
332+
PR_CHANGED="$(sanitize "$PR_CHANGED_RAW")"
333+
334+
if ! is_number "$BASE_OVERALL" || ! is_number "$PR_OVERALL"; then
335+
echo "Failed to parse coverage values: base='${BASE_OVERALL}', pr='${PR_OVERALL}'."
336+
exit 1
337+
fi
338+
339+
# 2) Compare metrics against thresholds
340+
DELTA=$(awk -v pr="$PR_OVERALL" -v base="$BASE_OVERALL" 'BEGIN { printf "%.4f", pr - base }')
341+
DELTA_OK=$(compare_float "${DELTA} >= ${MAX_DROP}")
342+
343+
CHANGED_STATUS="SKIPPED (no changed coverage value)"
344+
CHANGED_OK=1
345+
if [ -n "$PR_CHANGED" ] && [ "$PR_CHANGED" != "NaN" ]; then
346+
if ! is_number "$PR_CHANGED"; then
347+
echo "Failed to parse changed-files coverage: changed='${PR_CHANGED}'."
348+
exit 1
349+
fi
350+
CHANGED_OK=$(compare_float "${PR_CHANGED} > ${MIN_CHANGED}")
351+
if [ "$CHANGED_OK" -eq 1 ]; then
352+
CHANGED_STATUS="PASS (> ${MIN_CHANGED}%)"
353+
else
354+
CHANGED_STATUS="FAIL (<= ${MIN_CHANGED}%)"
355+
fi
356+
fi
357+
358+
# 3) Output base metrics (always visible in logs + step summary)
359+
OVERALL_STATUS="PASS (>= ${MAX_DROP}%)"
360+
if [ "$DELTA_OK" -ne 1 ]; then
361+
OVERALL_STATUS="FAIL (< ${MAX_DROP}%)"
362+
fi
363+
364+
METRICS_TEXT=$(cat <<EOF
365+
Changed Files Coverage: ${PR_CHANGED}%
366+
PR Overall Coverage: ${PR_OVERALL}%
367+
Base Overall Coverage: ${BASE_OVERALL}%
368+
Delta (PR - Base): ${DELTA}%
369+
Changed Files Gate: ${CHANGED_STATUS}
370+
Overall Delta Gate: ${OVERALL_STATUS}
371+
EOF
372+
)
373+
374+
echo "$METRICS_TEXT"
375+
376+
{
377+
echo "### Coverage Gate Metrics"
378+
echo ""
379+
echo "- Changed Files Coverage: ${PR_CHANGED}%"
380+
echo "- PR Overall Coverage: ${PR_OVERALL}%"
381+
echo "- Base Overall Coverage: ${BASE_OVERALL}%"
382+
echo "- Delta (PR - Base): ${DELTA}%"
383+
echo "- Changed Files Gate: ${CHANGED_STATUS}"
384+
echo "- Overall Delta Gate: ${OVERALL_STATUS}"
385+
} >> "$GITHUB_STEP_SUMMARY"
386+
387+
# 4) Decide CI pass/fail
388+
if [ "$DELTA_OK" -ne 1 ]; then
389+
echo "Coverage gate failed: overall coverage dropped more than 0.1%."
390+
echo "base=${BASE_OVERALL}% pr=${PR_OVERALL}% delta=${DELTA}%"
391+
exit 1
392+
fi
393+
394+
if [ -z "$PR_CHANGED" ] || [ "$PR_CHANGED" = "NaN" ]; then
395+
echo "No changed-files coverage value detected, skip changed-files gate."
396+
exit 0
397+
fi
398+
399+
if [ "$CHANGED_OK" -ne 1 ]; then
400+
echo "Coverage gate failed: changed files coverage must be > 60%."
401+
echo "changed=${PR_CHANGED}%"
402+
exit 1
403+
fi
404+
405+
echo "Coverage gates passed."

.github/workflows/pr-cancel.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ jobs:
3737
);
3838
3939
for (const run of runs) {
40-
const isTargetPr = !run.pull_requests?.length || run.pull_requests.some((pr) => pr.number === prNumber);
40+
if (!run) {
41+
continue;
42+
}
43+
const prs = Array.isArray(run.pull_requests) ? run.pull_requests : [];
44+
const isTargetPr = prs.length === 0 || prs.some((pr) => pr.number === prNumber);
4145
if (run.head_sha === headSha && isTargetPr) {
4246
await github.rest.actions.cancelWorkflowRun({
4347
owner: context.repo.owner,

0 commit comments

Comments
 (0)