-
Notifications
You must be signed in to change notification settings - Fork 283
201 lines (177 loc) · 7.79 KB
/
Copy pathcoverage.yml
File metadata and controls
201 lines (177 loc) · 7.79 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
name: Coverage
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
coverage:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Verify package-lock.json exists
run: |
if [ ! -f package-lock.json ]; then
npm install --package-lock-only --ignore-scripts
fi
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libvips-dev libjpeg-dev libpng-dev libtiff-dev libgif-dev librsvg2-dev libpoppler-cpp-dev libcairo2-dev libpango1.0-dev libjpeg8-dev build-essential
- name: Install dependencies
run: npm ci
- name: Wait for Postgres to be ready
run: |
for i in $(seq 1 30); do
pg_isready -h localhost -p 5432 -U test_user && break
echo "Waiting for Postgres... attempt $i/30"
sleep 1
done
- name: Run database migrations
run: npm run migrate:up 2>&1
continue-on-error: true
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
- name: Run tests with coverage
id: coverage
run: |
npm run test:coverage -- --coverageReporters=json-summary --coverageReporters=text --coverageReporters=lcov --maxWorkers=2 --bail --forceExit 2>&1 | tee coverage-output.txt
EXIT_CODE=${PIPESTATUS[0]}
echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
timeout-minutes: 10
continue-on-error: true
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
STELLAR_ISSUER_SECRET: S-DUMMY-SECRET-KEY-FOR-TESTING-PURPOSES-ONLY
NODE_ENV: test
NODE_OPTIONS: --experimental-vm-modules
CI: true
- name: Extract coverage metrics
id: metrics
run: |
if [ -f coverage/coverage-summary.json ]; then
STMTS=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.statements.pct)")
BRANCHES=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.branches.pct)")
FUNCS=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.functions.pct)")
LINES=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.lines.pct)")
else
STMTS="N/A"
BRANCHES="N/A"
FUNCS="N/A"
LINES="N/A"
fi
echo "statements=$STMTS" >> "$GITHUB_OUTPUT"
echo "branches=$BRANCHES" >> "$GITHUB_OUTPUT"
echo "functions=$FUNCS" >> "$GITHUB_OUTPUT"
echo "lines=$LINES" >> "$GITHUB_OUTPUT"
# Thresholds — keep in sync with jest.config.js
THRESHOLD_STMTS=70
THRESHOLD_BRANCHES=70
THRESHOLD_FUNCS=70
THRESHOLD_LINES=70
PASSED=true
if [ "$STMTS" != "N/A" ]; then
node -e "process.exit(($STMTS < $THRESHOLD_STMTS) ? 1 : 0)" || PASSED=false
node -e "process.exit(($BRANCHES < $THRESHOLD_BRANCHES) ? 1 : 0)" || PASSED=false
node -e "process.exit(($FUNCS < $THRESHOLD_FUNCS) ? 1 : 0)" || PASSED=false
node -e "process.exit(($LINES < $THRESHOLD_LINES) ? 1 : 0)" || PASSED=false
fi
echo "passed=$PASSED" >> "$GITHUB_OUTPUT"
echo "threshold_stmts=$THRESHOLD_STMTS" >> "$GITHUB_OUTPUT"
echo "threshold_branches=$THRESHOLD_BRANCHES" >> "$GITHUB_OUTPUT"
echo "threshold_funcs=$THRESHOLD_FUNCS" >> "$GITHUB_OUTPUT"
echo "threshold_lines=$THRESHOLD_LINES" >> "$GITHUB_OUTPUT"
- name: Comment coverage on PR
if: github.event_name == 'pull_request'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const stmts = '${{ steps.metrics.outputs.statements }}';
const branches = '${{ steps.metrics.outputs.branches }}';
const funcs = '${{ steps.metrics.outputs.functions }}';
const lines = '${{ steps.metrics.outputs.lines }}';
const passed = '${{ steps.metrics.outputs.passed }}' === 'true';
const thStmts = '${{ steps.metrics.outputs.threshold_stmts }}';
const thBranches = '${{ steps.metrics.outputs.threshold_branches }}';
const thFuncs = '${{ steps.metrics.outputs.threshold_funcs }}';
const thLines = '${{ steps.metrics.outputs.threshold_lines }}';
const icon = (val, threshold) => parseFloat(val) >= parseFloat(threshold) ? '✅' : '❌';
const status = passed ? '✅ Coverage thresholds met' : '❌ Coverage below thresholds';
const body = `## 📊 Coverage Report
${status}
| Metric | Current | Threshold | Status |
|--------|---------|-----------|--------|
| Statements | ${stmts}% | ${thStmts}% | ${icon(stmts, thStmts)} |
| Branches | ${branches}% | ${thBranches}% | ${icon(branches, thBranches)} |
| Functions | ${funcs}% | ${thFuncs}% | ${icon(funcs, thFuncs)} |
| Lines | ${lines}% | ${thLines}% | ${icon(lines, thLines)} |
> Coverage enforcement is configured in \`jest.config.js\`. Thresholds only go up — never down.
`.replace(/^ +/gm, '');
// Find and update existing comment or create new
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.includes('📊 Coverage Report'));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
- name: Enforce coverage thresholds
if: steps.metrics.outputs.passed == 'false'
run: |
echo "⚠️ Coverage is below the required thresholds."
echo ""
echo " Statements: ${{ steps.metrics.outputs.statements }}% (need ${{ steps.metrics.outputs.threshold_stmts }}%)"
echo " Branches: ${{ steps.metrics.outputs.branches }}% (need ${{ steps.metrics.outputs.threshold_branches }}%)"
echo " Functions: ${{ steps.metrics.outputs.functions }}% (need ${{ steps.metrics.outputs.threshold_funcs }}%)"
echo " Lines: ${{ steps.metrics.outputs.lines }}% (need ${{ steps.metrics.outputs.threshold_lines }}%)"
echo ""
echo "Please add tests to cover the untested code."