forked from marmot-protocol/mdk
-
Notifications
You must be signed in to change notification settings - Fork 0
219 lines (186 loc) · 8.13 KB
/
coverage.yml
File metadata and controls
219 lines (186 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
name: Coverage
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
inputs:
baseline_coverage:
description: "Manual baseline coverage % (for testing)"
required: false
default: "0"
env:
CARGO_TERM_COLOR: always
jobs:
coverage:
runs-on: ubuntu-latest
name: Test Coverage
permissions:
contents: read
pull-requests: write # Required for PR comments
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
key: coverage
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y pkg-config
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate coverage
run: |
echo "=== Generating Coverage Report ==="
mkdir -p coverage
# Run tests with coverage for all feature combinations
cargo llvm-cov --all-features --workspace --no-report
cargo llvm-cov --no-default-features --workspace --no-report
cargo llvm-cov --no-default-features --features mip04 --workspace --no-report
# Generate all report formats from the merged coverage data
cargo llvm-cov report --lcov --output-path coverage/lcov.info
cargo llvm-cov report --html --output-dir coverage/html
cargo llvm-cov report | tee coverage/summary.txt
echo ""
echo "=== Coverage Summary ==="
cat coverage/summary.txt
- name: Extract coverage percentage
id: coverage
run: |
chmod +x scripts/extract-coverage.sh
COVERAGE=$(./scripts/extract-coverage.sh coverage/lcov.info)
echo "percentage=$COVERAGE" >> "$GITHUB_OUTPUT"
echo "Current coverage: $COVERAGE%"
- name: Get master branch coverage (for PRs)
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
id: master_coverage
run: |
# Check if manual baseline provided (for testing)
if [ -n "${{ github.event.inputs.baseline_coverage }}" ]; then
MASTER_COV="${{ github.event.inputs.baseline_coverage }}"
echo "baseline=$MASTER_COV" >> "$GITHUB_OUTPUT"
echo "Using manual baseline: $MASTER_COV%"
else
echo "🔍 Searching for baseline from master branch..."
# Get the latest completed coverage workflow run ID from master
RUN_ID=$(gh run list \
--repo ${{ github.repository }} \
--workflow=coverage.yml \
--branch master \
--status completed \
--limit 1 \
--json databaseId \
--jq '.[0].databaseId')
if [ -z "$RUN_ID" ] || [ "$RUN_ID" = "null" ]; then
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ No completed coverage workflow run found on master"
exit 0
fi
echo "Found workflow run ID: $RUN_ID"
# Download the baseline artifact from that specific run
if gh run download "$RUN_ID" \
--repo ${{ github.repository }} \
--name coverage-baseline \
--dir master-coverage; then
if [ -f master-coverage/coverage-baseline.txt ]; then
MASTER_COV=$(cat master-coverage/coverage-baseline.txt)
echo "baseline=$MASTER_COV" >> "$GITHUB_OUTPUT"
echo "✓ Master branch coverage: $MASTER_COV%"
else
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ Baseline file not found in artifact"
fi
else
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ Failed to download baseline artifact from run $RUN_ID"
fi
fi
env:
GH_TOKEN: ${{ github.token }}
- name: Compare coverage
if: (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch') && steps.master_coverage.outputs.baseline != '0'
run: |
CURRENT=${{ steps.coverage.outputs.percentage }}
BASELINE=${{ steps.master_coverage.outputs.baseline }}
echo "=== Coverage Comparison ==="
echo "Master branch: $BASELINE%"
echo "This PR: $CURRENT%"
DIFF=$(awk "BEGIN {printf \"%.2f\", $CURRENT - $BASELINE}")
if (( $(awk "BEGIN {print ($CURRENT < $BASELINE)}") )); then
echo "❌ Coverage decreased by $DIFF%"
echo "::error::Coverage regression detected. Coverage decreased from $BASELINE% to $CURRENT% (-$DIFF%)"
exit 1
elif (( $(awk "BEGIN {print ($CURRENT > $BASELINE)}") )); then
echo "✅ Coverage improved by $DIFF%"
else
echo "✅ Coverage maintained at $BASELINE%"
fi
- name: Comment PR with coverage change
if: always() && github.event_name == 'pull_request' && steps.master_coverage.outputs.baseline != '0'
uses: actions/github-script@v7
with:
script: |
const current = parseFloat('${{ steps.coverage.outputs.percentage }}');
const baseline = parseFloat('${{ steps.master_coverage.outputs.baseline }}');
const diff = (current - baseline).toFixed(2);
let emoji = current > baseline ? '✅' : current < baseline ? '❌' : '⚠️';
// Build the message (with markdown header for comments, without for logs)
const commentBody = `## ${emoji} Coverage: ${baseline}% → ${current}% (${diff > 0 ? '+' : ''}${diff}%)`;
const logMessage = `${emoji} Coverage: ${baseline}% → ${current}% (${diff > 0 ? '+' : ''}${diff}%)`;
// Skip commenting on PRs from forks (they don't have write permissions)
const isFork = context.payload.pull_request.head.repo.full_name !== context.payload.repository.full_name;
if (isFork) {
console.log('Skipping PR comment: PR is from a fork and does not have write permissions');
console.log('Coverage result:', logMessage);
return;
}
try {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
} catch (error) {
// Gracefully handle permission errors (e.g., from forks)
if (error.status === 403) {
console.log('Unable to comment on PR (likely from fork):', error.message);
console.log('Coverage result:', logMessage);
} else {
throw error;
}
}
- name: Save coverage baseline (master only)
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
run: |
mkdir -p coverage-baseline
echo "${{ steps.coverage.outputs.percentage }}" > coverage-baseline/coverage-baseline.txt
echo "Saved baseline: ${{ steps.coverage.outputs.percentage }}%"
- name: Upload coverage baseline (master only)
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
uses: actions/upload-artifact@v4
with:
name: coverage-baseline
path: coverage-baseline/coverage-baseline.txt
retention-days: 90
- name: Upload HTML coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report-html
path: coverage/html/
retention-days: 90
- name: Upload lcov coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report-lcov
path: coverage/lcov.info
retention-days: 90
- name: Upload coverage summary
uses: actions/upload-artifact@v4
with:
name: coverage-summary
path: coverage/summary.txt
retention-days: 90