-
Notifications
You must be signed in to change notification settings - Fork 0
379 lines (324 loc) · 13.1 KB
/
pr-validation.yml
File metadata and controls
379 lines (324 loc) · 13.1 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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
name: PR Validation
on:
pull_request:
branches: [ "main" ]
types: [opened, synchronize, reopened, ready_for_review]
# Ensure only one workflow runs per PR
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
# Validate PR title follows Conventional Commits
validate-pr-title:
name: Validate PR Title
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false && github.event.pull_request.user.login != 'dependabot[bot]'
steps:
- name: Validate PR title
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false
subjectPattern: ^[A-Z].+$
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
starts with an uppercase character.
# Validate PR has description
validate-pr-description:
name: Validate PR Description
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false && github.event.pull_request.user.login != 'dependabot[bot]'
steps:
- name: Check PR description
uses: actions/github-script@v8
with:
script: |
const prBody = context.payload.pull_request.body || '';
const minLength = 50;
if (prBody.trim().length < minLength) {
core.setFailed(
`PR description is too short (${prBody.trim().length} chars). ` +
`Please provide a meaningful description (minimum ${minLength} chars).`
);
return;
}
// Check for required sections (flexible check)
const hasWhat = /###?\s*What/i.test(prBody);
const hasWhy = /###?\s*Why/i.test(prBody);
const hasTesting = /###?\s*Testing/i.test(prBody) || /\[x\].*test/i.test(prBody);
if (!hasWhat || !hasWhy) {
core.setFailed(
'PR description is missing required sections. ' +
'Please use the PR template and fill in: What, Why, and Testing sections.'
);
return;
}
core.info('✅ PR description looks good!');
# Check PR size
check-pr-size:
name: Check PR Size
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Check PR size
uses: actions/github-script@v8
with:
script: |
const pr = context.payload.pull_request;
const additions = pr.additions || 0;
const deletions = pr.deletions || 0;
const totalChanges = additions + deletions;
// Soft limit (warning) and hard limit (failure)
const hardLimit = 1500;
const softLimit = 400;
if (totalChanges > hardLimit) {
core.setFailed(
`⚠️ PR is too large (${totalChanges} lines changed). ` +
`Please consider breaking it into smaller PRs (< ${hardLimit} lines).`
);
return;
}
if (totalChanges > softLimit) {
core.warning(
`⚠️ PR is getting large (${totalChanges} lines changed). ` +
`Consider breaking it into smaller PRs for easier review.`
);
} else {
core.info(`✅ PR size is good (${totalChanges} lines changed)`);
}
# Lint PR changes
lint-check:
name: Lint Check
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Restore
run: dotnet restore
- name: Get changed C# files
id: changed-files
run: |
# Get list of changed .cs files in the PR
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT origin/${{ github.event.pull_request.base.ref }}...HEAD | grep '\.cs$' || true)
if [ -z "$CHANGED_FILES" ]; then
echo "has_cs_files=false" >> $GITHUB_OUTPUT
echo "ℹ️ No C# files changed in this PR"
else
echo "has_cs_files=true" >> $GITHUB_OUTPUT
# Convert newlines to spaces and store
FILES_SPACE_SEPARATED=$(echo "$CHANGED_FILES" | tr '\n' ' ')
echo "files=$FILES_SPACE_SEPARATED" >> $GITHUB_OUTPUT
echo "📝 Changed C# files:"
echo "$CHANGED_FILES"
fi
- name: Format check changed files
if: steps.changed-files.outputs.has_cs_files == 'true'
run: |
# Check formatting only for changed files
FILES="${{ steps.changed-files.outputs.files }}"
echo "🔍 Checking formatting for changed files..."
echo "Files: $FILES"
# Run format check with --include for each file
dotnet format --verify-no-changes --include $FILES --verbosity diagnostic
if [ $? -ne 0 ]; then
echo ""
echo "❌ Code formatting issues detected in your changes."
echo "Please run the following command locally:"
echo " dotnet format --include $FILES"
echo ""
exit 1
fi
echo "✅ Code formatting is correct for all changed files"
- name: Skip format check
if: steps.changed-files.outputs.has_cs_files == 'false'
run: |
echo "✅ No C# files to check - skipping format validation"
# Security scan
security-scan:
name: Security Scan
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Restore
run: dotnet restore
- name: Check for vulnerable packages
run: |
echo "🔍 Scanning for vulnerable packages..."
dotnet list package --vulnerable --include-transitive 2>&1 | tee vulnerability-report.txt
if grep -qi "critical\|high" vulnerability-report.txt; then
echo "❌ Critical or High severity vulnerabilities detected!"
echo "Please review and update dependencies before merging."
exit 1
else
echo "✅ No critical or high severity vulnerabilities found"
fi
# Bundle size quality gate
bundle-size-check:
name: Bundle Size Check
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Install WASM workloads
run: dotnet workload install wasm-experimental wasm-tools
- name: Restore dependencies
run: dotnet restore
- name: Build and Publish Release (Trimmed)
run: |
dotnet publish Abies.Conduit/Abies.Conduit.csproj -c Release -o ./publish-trimmed
- name: Measure Bundle Size
id: bundle-size
run: |
# Measure the _framework directory size (WASM bundle)
FRAMEWORK_DIR="./publish-trimmed/wwwroot/_framework"
if [ -d "$FRAMEWORK_DIR" ]; then
# Get total size in bytes
TOTAL_BYTES=$(du -sb "$FRAMEWORK_DIR" | cut -f1)
TOTAL_MB=$((TOTAL_BYTES / 1024 / 1024))
FILE_COUNT=$(find "$FRAMEWORK_DIR" -type f | wc -l)
echo "bundle_size_mb=$TOTAL_MB" >> $GITHUB_OUTPUT
echo "bundle_size_bytes=$TOTAL_BYTES" >> $GITHUB_OUTPUT
echo "file_count=$FILE_COUNT" >> $GITHUB_OUTPUT
echo "📦 WASM Bundle Size Report"
echo "=========================="
echo "Total Size: ${TOTAL_MB}MB ($TOTAL_BYTES bytes)"
echo "File Count: $FILE_COUNT"
echo ""
echo "Largest files:"
find "$FRAMEWORK_DIR" -type f -exec du -h {} + | sort -rh | head -10
else
echo "❌ Framework directory not found at $FRAMEWORK_DIR"
exit 1
fi
- name: Check Bundle Size Limits
run: |
BUNDLE_SIZE=${{ steps.bundle-size.outputs.bundle_size_mb }}
# Validate BUNDLE_SIZE is a valid integer
if ! echo "$BUNDLE_SIZE" | grep -qE '^[0-9]+$'; then
echo "❌ FAILED: Could not determine bundle size (got '$BUNDLE_SIZE')"
exit 1
fi
# Hard limit: 15MB for trimmed Release build
HARD_LIMIT=15
# Soft limit (warning): 10MB
SOFT_LIMIT=10
echo "📊 Bundle Size: ${BUNDLE_SIZE}MB"
echo "🔴 Hard Limit: ${HARD_LIMIT}MB"
echo "🟡 Soft Limit: ${SOFT_LIMIT}MB"
if [ "$BUNDLE_SIZE" -gt "$HARD_LIMIT" ]; then
echo ""
echo "❌ FAILED: Bundle size (${BUNDLE_SIZE}MB) exceeds hard limit (${HARD_LIMIT}MB)"
echo ""
echo "The WASM bundle is too large. Please:"
echo "1. Ensure PublishTrimmed=true is set for Release configuration"
echo "2. Review and remove unnecessary dependencies"
echo "3. Enable InvariantGlobalization if not already done"
echo "4. Consider code splitting if applicable"
exit 1
elif [ "$BUNDLE_SIZE" -gt "$SOFT_LIMIT" ]; then
echo ""
echo "⚠️ WARNING: Bundle size (${BUNDLE_SIZE}MB) exceeds soft limit (${SOFT_LIMIT}MB)"
echo "Consider optimizing bundle size for faster startup times."
else
echo ""
echo "✅ Bundle size is within acceptable limits"
fi
# Check for TODO/FIXME without issues
check-todos:
name: Check TODOs
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for untracked TODOs
run: |
# Find TODO/FIXME comments without issue references
untracked=$(grep -rn "TODO\|FIXME" --include="*.cs" --include="*.fs" --exclude-dir=obj --exclude-dir=bin . | grep -v "#[0-9]" || true)
if [ ! -z "$untracked" ]; then
echo "⚠️ Found TODO/FIXME comments without issue references:"
echo "$untracked"
echo ""
echo "Please either:"
echo "1. Create an issue and reference it (e.g., // TODO #123: description)"
echo "2. Fix the item in this PR"
echo "3. Remove the comment if not needed"
# For now, just warn
# exit 1
else
echo "✅ No untracked TODOs found"
fi
# Summary
pr-validation-summary:
name: PR Validation Summary
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
needs: [validate-pr-title, validate-pr-description, check-pr-size, lint-check, security-scan, bundle-size-check, check-todos]
steps:
- name: Check if automated PR
id: check-automated
run: |
if [ "${{ github.event.pull_request.user.login }}" = "dependabot[bot]" ]; then
echo "automated=true" >> $GITHUB_OUTPUT
else
echo "automated=false" >> $GITHUB_OUTPUT
fi
- name: All checks passed
run: |
if [ "${{ steps.check-automated.outputs.automated }}" = "true" ]; then
echo "🤖 Automated PR validation summary"
echo "✅ PR size is reasonable"
echo "✅ Code formatting is correct"
echo "✅ No security vulnerabilities"
echo "✅ Bundle size within limits"
echo "✅ No untracked TODOs"
echo ""
echo "Note: Title and description checks skipped for automated PRs"
else
echo "🎉 All PR validation checks passed!"
echo "✅ PR title follows Conventional Commits"
echo "✅ PR has adequate description"
echo "✅ PR size is reasonable"
echo "✅ Code formatting is correct"
echo "✅ No security vulnerabilities"
echo "✅ Bundle size within limits"
echo "✅ No untracked TODOs"
echo ""
echo "Next steps:"
echo "1. Wait for CD and E2E workflows to complete"
echo "2. Request review from team members"
echo "3. Address any feedback"
echo "4. Merge when approved!"
fi