@@ -25,30 +25,17 @@ jobs:
2525 run : composer install --prefer-dist --no-progress
2626
2727 - name : Run tests with coverage
28- run : ./vendor/bin/phpunit --testsuite Unit --coverage-clover=coverage/clover .xml --coverage-text | tee coverage/summary.txt
28+ run : ./vendor/bin/phpunit --testsuite Unit --coverage-clover=coverage/unit .xml
2929
30- - name : Check coverage threshold
31- run : |
32- LINE_PCT=$(grep -oP 'Lines:\s+\K[\d.]+' coverage/summary.txt | head -1)
33- echo "Line coverage: ${LINE_PCT}%"
34- if [ "$(echo "$LINE_PCT < 80" | bc -l)" -eq 1 ]; then
35- echo "::error::Coverage ${LINE_PCT}% is below 80% minimum"
36- exit 1
37- fi
38-
39- - name : Upload coverage artifact
40- if : github.event_name == 'push' && github.ref == 'refs/heads/main'
30+ - name : Upload coverage
4131 uses : actions/upload-artifact@v4
4232 with :
43- name : coverage-baseline
44- path : coverage/clover.xml
45- retention-days : 90
33+ name : coverage-unit
34+ path : coverage/unit.xml
4635
47- coverage-diff :
48- name : Coverage Diff
36+ integration :
37+ name : Integration Tests
4938 runs-on : ubuntu-latest
50- if : github.event_name == 'pull_request'
51- needs : unit
5239
5340 steps :
5441 - name : Checkout
@@ -64,10 +51,76 @@ jobs:
6451 - name : Install dependencies
6552 run : composer install --prefer-dist --no-progress
6653
67- - name : Generate PR coverage
68- run : ./vendor/bin/phpunit --testsuite Unit --coverage-clover=coverage/clover.xml --coverage-text | tee coverage/summary.txt
54+ - name : Run tests with coverage
55+ run : ./vendor/bin/phpunit --testsuite Integration --coverage-clover=coverage/integration.xml
56+
57+ - name : Upload coverage
58+ uses : actions/upload-artifact@v4
59+ with :
60+ name : coverage-integration
61+ path : coverage/integration.xml
62+
63+ coverage :
64+ name : Coverage Check
65+ runs-on : ubuntu-latest
66+ needs : [unit, integration]
67+
68+ steps :
69+ - name : Download unit coverage
70+ uses : actions/download-artifact@v4
71+ with :
72+ name : coverage-unit
73+ path : coverage
74+
75+ - name : Download integration coverage
76+ uses : actions/download-artifact@v4
77+ with :
78+ name : coverage-integration
79+ path : coverage
80+
81+ - name : Merge and check coverage
82+ run : |
83+ merge_clover() {
84+ local total_stmts=0 covered_stmts=0
85+ for f in "$@"; do
86+ [ -f "$f" ] || continue
87+ s=$(grep -oP 'statements="\K\d+' "$f" | paste -sd+ | bc)
88+ c=$(grep -oP 'coveredstatements="\K\d+' "$f" | paste -sd+ | bc)
89+ total_stmts=$((total_stmts + s))
90+ covered_stmts=$((covered_stmts + c))
91+ done
92+ echo "$covered_stmts $total_stmts"
93+ }
94+
95+ read COVERED TOTAL <<< $(merge_clover coverage/unit.xml coverage/integration.xml)
96+ PCT=$(echo "scale=2; $COVERED * 100 / $TOTAL" | bc)
97+ echo "Coverage: ${PCT}% ($COVERED / $TOTAL statements)"
98+
99+ if [ "$(echo "$PCT < 80" | bc -l)" -eq 1 ]; then
100+ echo "::error::Coverage ${PCT}% is below 80% minimum"
101+ exit 1
102+ fi
69103
70- - name : Download baseline coverage
104+ - name : Upload merged baseline
105+ if : github.event_name == 'push' && github.ref == 'refs/heads/main'
106+ run : |
107+ echo "$COVERED $TOTAL" > coverage/baseline.txt
108+ env :
109+ COVERED : ${{ env.COVERED }}
110+ TOTAL : ${{ env.TOTAL }}
111+
112+ - name : Save baseline artifact
113+ if : github.event_name == 'push' && github.ref == 'refs/heads/main'
114+ uses : actions/upload-artifact@v4
115+ with :
116+ name : coverage-baseline
117+ path : |
118+ coverage/unit.xml
119+ coverage/integration.xml
120+ retention-days : 90
121+
122+ - name : Download baseline (PR only)
123+ if : github.event_name == 'pull_request'
71124 uses : dawidd6/action-download-artifact@v6
72125 with :
73126 name : coverage-baseline
@@ -76,25 +129,36 @@ jobs:
76129 workflow : tests.yml
77130 if_no_artifact_found : warn
78131
79- - name : Compare coverage
132+ - name : Check coverage diff (PR only)
133+ if : github.event_name == 'pull_request'
80134 run : |
81- PR_PCT=$(grep -oP 'Lines:\s+\K[\d.]+' coverage/summary.txt | head -1)
82- echo "PR coverage: ${PR_PCT}%"
83-
84- if [ ! -f coverage/baseline/clover.xml ]; then
135+ if [ ! -f coverage/baseline/unit.xml ]; then
85136 echo "No baseline found, skipping diff check"
86137 exit 0
87138 fi
88139
89- BASE_STATEMENTS=$(grep -oP 'statements="\K\d+' coverage/baseline/clover.xml | paste -sd+ | bc)
90- BASE_COVERED=$(grep -oP 'coveredstatements="\K\d+' coverage/baseline/clover.xml | paste -sd+ | bc)
91- BASE_PCT=$(echo "scale=2; $BASE_COVERED * 100 / $BASE_STATEMENTS" | bc)
140+ merge_clover() {
141+ local total_stmts=0 covered_stmts=0
142+ for f in "$@"; do
143+ [ -f "$f" ] || continue
144+ s=$(grep -oP 'statements="\K\d+' "$f" | paste -sd+ | bc)
145+ c=$(grep -oP 'coveredstatements="\K\d+' "$f" | paste -sd+ | bc)
146+ total_stmts=$((total_stmts + s))
147+ covered_stmts=$((covered_stmts + c))
148+ done
149+ echo "$covered_stmts $total_stmts"
150+ }
151+
152+ read BASE_COV BASE_TOTAL <<< $(merge_clover coverage/baseline/unit.xml coverage/baseline/integration.xml)
153+ BASE_PCT=$(echo "scale=2; $BASE_COV * 100 / $BASE_TOTAL" | bc)
154+
155+ read PR_COV PR_TOTAL <<< $(merge_clover coverage/unit.xml coverage/integration.xml)
156+ PR_PCT=$(echo "scale=2; $PR_COV * 100 / $PR_TOTAL" | bc)
92157
93- echo "Baseline coverage: ${BASE_PCT}%"
94158 DIFF=$(echo "$PR_PCT - $BASE_PCT" | bc -l)
95- echo "Delta : ${DIFF}%"
159+ echo "Baseline : ${BASE_PCT}% → PR: ${PR_PCT}% (${ DIFF}%) "
96160
97161 if [ "$(echo "$DIFF < -1" | bc -l)" -eq 1 ]; then
98- echo "::error::Coverage dropped ${DIFF}% (baseline ${BASE_PCT}% → PR ${PR_PCT}%). Maximum allowed drop is 1%. "
162+ echo "::error::Coverage dropped ${DIFF}% (max allowed: -1%) "
99163 exit 1
100164 fi
0 commit comments