Skip to content

Commit 255142d

Browse files
authored
ci: add coverage regression gate (#1689)
Signed-off-by: Erez Freiberger <enoodle@gmail.com>
1 parent 7329806 commit 255142d

2 files changed

Lines changed: 118 additions & 8 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright 2025 NVIDIA CORPORATION
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: KAI Scheduler - Coverage Gate
5+
6+
on:
7+
workflow_run:
8+
workflows: ["KAI Scheduler - Pull Request"]
9+
types:
10+
- completed
11+
pull_request:
12+
types: [labeled, unlabeled]
13+
14+
jobs:
15+
coverage-gate:
16+
name: Coverage Gate
17+
runs-on: ubuntu-latest
18+
if: >-
19+
(github.event_name == 'workflow_run' &&
20+
github.event.workflow_run.event == 'pull_request' &&
21+
github.event.workflow_run.conclusion == 'success') ||
22+
github.event_name == 'pull_request'
23+
permissions:
24+
actions: read
25+
contents: read
26+
issues: read
27+
pull-requests: read
28+
29+
steps:
30+
- name: Download PR number artifact
31+
if: github.event_name == 'workflow_run'
32+
continue-on-error: true
33+
uses: actions/download-artifact@v8
34+
with:
35+
name: pr-number-for-comment
36+
run-id: ${{ github.event.workflow_run.id }}
37+
github-token: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Download coverage report artifact
40+
if: github.event_name == 'workflow_run'
41+
continue-on-error: true
42+
uses: actions/download-artifact@v8
43+
with:
44+
name: coverage-report-for-comment
45+
run-id: ${{ github.event.workflow_run.id }}
46+
github-token: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Resolve coverage gate state
49+
id: coverage_state
50+
env:
51+
EVENT_NAME: ${{ github.event_name }}
52+
GH_TOKEN: ${{ github.token }}
53+
LABEL_EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
54+
LABEL_EVENT_HAS_SKIP_LABEL: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'skip-coverage-gate') }}
55+
REPOSITORY: ${{ github.repository }}
56+
run: |
57+
if [ "$EVENT_NAME" = "workflow_run" ]; then
58+
if [ ! -s pr_number.txt ] || [ ! -s coverage-report.txt ]; then
59+
echo "No coverage report artifact found. Skipping coverage gate."
60+
echo "has_delta=false" >> $GITHUB_OUTPUT
61+
exit 0
62+
fi
63+
64+
PR_NUMBER=$(cat pr_number.txt)
65+
DELTA=$(grep -Eo 'delta -?[0-9]+([.][0-9]+)?%' coverage-report.txt | head -1 | grep -Eo -- '-?[0-9]+([.][0-9]+)?' || true)
66+
67+
if gh api "repos/${REPOSITORY}/issues/${PR_NUMBER}" --jq '.labels[].name' | grep -Fxq 'skip-coverage-gate'; then
68+
HAS_SKIP_LABEL=true
69+
else
70+
HAS_SKIP_LABEL=false
71+
fi
72+
else
73+
PR_NUMBER="$LABEL_EVENT_PR_NUMBER"
74+
HAS_SKIP_LABEL="$LABEL_EVENT_HAS_SKIP_LABEL"
75+
COMMENTS=$(gh api "repos/${REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate)
76+
DELTA=$(echo "$COMMENTS" | jq -r '
77+
[
78+
.[]
79+
| select(.user.login == "github-actions[bot]")
80+
| select(.body | contains("<!-- code-coverage-report-comment -->"))
81+
]
82+
| sort_by(.updated_at)
83+
| last
84+
| .body? // ""
85+
| (capture("delta (?<delta>-?[0-9]+([.][0-9]+)?)%")? // {})
86+
| .delta // empty
87+
')
88+
fi
89+
90+
if [ -z "$DELTA" ] || [ "$DELTA" = "null" ]; then
91+
echo "No existing coverage delta found. Coverage will be evaluated on the next code-change run."
92+
exit 0
93+
fi
94+
95+
echo "delta=$DELTA" >> $GITHUB_OUTPUT
96+
echo "has_skip_label=$HAS_SKIP_LABEL" >> $GITHUB_OUTPUT
97+
98+
if awk -v delta="$DELTA" 'BEGIN { exit !((delta + 0) < 0) }'; then
99+
echo "delta_negative=true" >> $GITHUB_OUTPUT
100+
else
101+
echo "delta_negative=false" >> $GITHUB_OUTPUT
102+
fi
103+
104+
- name: Fail on coverage regression
105+
if: steps.coverage_state.outputs.delta_negative == 'true' && steps.coverage_state.outputs.has_skip_label != 'true'
106+
run: |
107+
echo "Coverage regression detected: delta=${{ steps.coverage_state.outputs.delta }}%"
108+
echo "Add the skip-coverage-gate label to bypass this gate when appropriate."
109+
exit 1

.github/workflows/on-pr.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,22 @@ jobs:
121121
122122
BASELINE_COVERAGE=$(grep -oE '[0-9]+\.[0-9]+%' coverage-badge/badges/coverage.svg | head -1 | tr -d '%')
123123
echo "baseline=$BASELINE_COVERAGE" >> $GITHUB_OUTPUT
124+
125+
DELTA=$(awk -v pr="$PR_COVERAGE" -v baseline="$BASELINE_COVERAGE" 'BEGIN { printf "%.2f", pr - baseline }')
126+
echo "delta=$DELTA" >> $GITHUB_OUTPUT
124127
- name: Save coverage report to file
125128
env:
126129
REPORT_BODY: ${{ steps.coverage_reporter.outputs.coverage_report }}
127130
BASELINE_COVERAGE: ${{ steps.coverage_totals.outputs.baseline }}
128131
PR_COVERAGE: ${{ steps.coverage_totals.outputs.pr }}
132+
COVERAGE_DELTA: ${{ steps.coverage_totals.outputs.delta }}
129133
run: |
130-
if [ -z "$REPORT_BODY" ]; then
131-
: > coverage-report.txt
132-
exit 0
133-
fi
134-
135134
{
136-
echo "**Total coverage:** ${BASELINE_COVERAGE}% -> ${PR_COVERAGE}%"
137-
echo ""
138-
echo "$REPORT_BODY"
135+
echo "**Total coverage:** ${BASELINE_COVERAGE}% -> ${PR_COVERAGE}% (delta ${COVERAGE_DELTA}%)"
136+
if [ -n "$REPORT_BODY" ]; then
137+
echo ""
138+
echo "$REPORT_BODY"
139+
fi
139140
} > coverage-report.txt
140141
- name: Upload coverage report
141142
uses: actions/upload-artifact@v7

0 commit comments

Comments
 (0)