-
-
Notifications
You must be signed in to change notification settings - Fork 10
384 lines (330 loc) · 15.5 KB
/
pr-into-dev.yml
File metadata and controls
384 lines (330 loc) · 15.5 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
380
381
382
383
384
# ─────────────────────────────────────────────────────────────────
# PR into Dev Workflow
# ─────────────────────────────────────────────────────────────────
# Validates pull requests before merging to dev.
#
# Validations:
# - Source branch must be feature/*, fix/*, hotfix/*, or test/*
# - PR title must follow conventional commit format
# - At least one issue must be linked
# - All quality gates must pass
# - Fork PRs run read-only checks
#
# Author: Alireza Rezvani
# Date: 2025-11-07
# ─────────────────────────────────────────────────────────────────
name: PR into Dev
on:
pull_request:
types:
- opened
- synchronize
- ready_for_review
branches:
- dev
permissions:
contents: read
pull-requests: write
issues: read
statuses: write
# Cancel in-progress runs for the same PR
concurrency:
group: pr-dev-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
# ─────────────────────────────────────────────────────────────────
# Fork Safety Check
# ─────────────────────────────────────────────────────────────────
fork-check:
name: Check Fork Status
runs-on: ubuntu-latest
outputs:
is-fork: ${{ steps.fork-safety.outputs.is-fork }}
should-skip-writes: ${{ steps.fork-safety.outputs.should-skip-writes }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check if PR is from fork
id: fork-safety
uses: ./.github/actions/fork-safety
- name: Log fork status
run: |
if [[ "${{ steps.fork-safety.outputs.is-fork }}" == "true" ]]; then
echo "⚠️ This PR is from a fork - write operations will be skipped for security"
else
echo "✅ This PR is from the same repository"
fi
# ─────────────────────────────────────────────────────────────────
# Branch Name Validation
# ─────────────────────────────────────────────────────────────────
validate-branch:
name: Validate Branch Name
runs-on: ubuntu-latest
steps:
- name: Validate source branch name
run: |
BRANCH_NAME="${{ github.head_ref }}"
echo "🔍 Validating branch name: $BRANCH_NAME"
# Check if branch follows naming convention
if [[ ! "$BRANCH_NAME" =~ ^(feature|fix|hotfix|test)/ ]]; then
echo "❌ Invalid branch name: $BRANCH_NAME"
echo ""
echo "Branch must start with one of:"
echo " - feature/ (for new features)"
echo " - fix/ (for bug fixes)"
echo " - hotfix/ (for critical fixes)"
echo " - test/ (for workflow testing)"
echo ""
echo "Example: feature/issue-123-add-user-auth"
exit 1
fi
echo "✅ Branch name is valid: $BRANCH_NAME"
# ─────────────────────────────────────────────────────────────────
# PR Title Validation (Conventional Commits)
# ─────────────────────────────────────────────────────────────────
validate-pr-title:
name: Validate PR Title
runs-on: ubuntu-latest
steps:
- name: Validate conventional commit format
uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ 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.
- name: Add comment on invalid title
if: failure()
uses: actions/github-script@v8
with:
github-token: ${{ github.token }}
script: |
const prNumber = context.payload.pull_request.number;
const comment = `## ❌ Invalid PR Title
Your PR title doesn't follow the conventional commit format.
**Required format:** \`<type>(<scope>): <subject>\`
**Valid types:**
- \`feat\`: New feature
- \`fix\`: Bug fix
- \`docs\`: Documentation changes
- \`style\`: Code style changes (formatting, etc.)
- \`refactor\`: Code refactoring
- \`perf\`: Performance improvements
- \`test\`: Adding or updating tests
- \`build\`: Build system changes
- \`ci\`: CI/CD changes
- \`chore\`: Other changes
- \`revert\`: Revert a previous commit
**Examples:**
- \`feat(auth): Add user authentication\`
- \`fix(api): Resolve null pointer exception\`
- \`docs: Update README with setup instructions\`
**Subject rules:**
- Start with uppercase letter
- Be concise and descriptive
- Use imperative mood ("Add" not "Added")
Please update your PR title and I'll re-check automatically.`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: comment
});
# ─────────────────────────────────────────────────────────────────
# Linked Issue Validation
# ─────────────────────────────────────────────────────────────────
validate-linked-issue:
name: Validate Linked Issue
runs-on: ubuntu-latest
steps:
- name: Check for linked issues
id: check-linked-issue
uses: actions/github-script@v8
with:
github-token: ${{ github.token }}
script: |
const prNumber = context.payload.pull_request.number;
const prBody = context.payload.pull_request.body || '';
// Regex to find linked issues: Closes #123, Fixes #456, Relates to #789
const issueRegex = /(close[sd]?|fix(e[sd])?|resolve[sd]?|relates?\s+to)\s+#(\d+)/gi;
const matches = [...prBody.matchAll(issueRegex)];
if (matches.length === 0) {
core.setFailed('No linked issues found in PR description');
core.setOutput('has-linked-issue', 'false');
return;
}
const issueNumbers = matches.map(m => m[3]);
console.log(`✅ Found ${matches.length} linked issue(s): #${issueNumbers.join(', #')}`);
core.setOutput('has-linked-issue', 'true');
core.setOutput('issue-numbers', issueNumbers.join(','));
- name: Add comment on missing linked issue
if: failure()
uses: actions/github-script@v8
with:
github-token: ${{ github.token }}
script: |
const prNumber = context.payload.pull_request.number;
const comment = `## ❌ No Linked Issue Found
This PR must be linked to at least one issue for automated tracking.
**How to link an issue:**
Add one of the following keywords to your PR description:
- \`Closes #123\` - Closes the issue when PR is merged
- \`Fixes #456\` - Closes the issue when PR is merged
- \`Resolves #789\` - Closes the issue when PR is merged
- \`Relates to #101\` - References the issue without closing
**Example:**
\`\`\`markdown
## Summary
This PR adds user authentication.
Closes #123
Relates to #124
\`\`\`
**Why this is required:**
- Enables automated status tracking on the project board
- Links code changes to requirements
- Maintains project traceability
Please update your PR description with a linked issue.`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: comment
});
# ─────────────────────────────────────────────────────────────────
# Rate Limit Check (Before Running Quality Checks)
# ─────────────────────────────────────────────────────────────────
rate-limit-check:
name: Check API Rate Limit
runs-on: ubuntu-latest
needs:
- fork-check
if: needs.fork-check.outputs.should-skip-writes != 'true'
outputs:
can-proceed: ${{ steps.rate-limit.outputs.can-proceed }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check rate limit
id: rate-limit
uses: ./.github/actions/rate-limit-check
with:
minimum-remaining: 50
github-token: ${{ github.token }}
- name: Log rate limit status
run: |
if [[ "${{ steps.rate-limit.outputs.can-proceed }}" == "false" ]]; then
echo "⚠️ Rate limit too low: ${{ steps.rate-limit.outputs.remaining }} remaining"
echo "Waiting until: ${{ steps.rate-limit.outputs.reset-time }}"
exit 1
fi
echo "✅ Rate limit OK: ${{ steps.rate-limit.outputs.remaining }} calls remaining"
# ─────────────────────────────────────────────────────────────────
# Quality Gates (Reusable Workflow)
# ─────────────────────────────────────────────────────────────────
quality-checks:
name: Run Quality Checks
needs:
- validate-branch
- validate-pr-title
- validate-linked-issue
- rate-limit-check
if: |
always() &&
needs.validate-branch.result == 'success' &&
needs.validate-pr-title.result == 'success' &&
needs.validate-linked-issue.result == 'success' &&
needs.rate-limit-check.result == 'success'
uses: ./.github/workflows/reusable-pr-checks.yml
with:
mobile_check: false
integration_tests: false
node_version: '20'
pnpm_version: '9'
# ─────────────────────────────────────────────────────────────────
# Final Status Check
# ─────────────────────────────────────────────────────────────────
final-status:
name: Final PR Status
runs-on: ubuntu-latest
needs:
- fork-check
- validate-branch
- validate-pr-title
- validate-linked-issue
- quality-checks
if: always()
steps:
- name: Generate final summary
run: |
echo "# 🎯 PR Validation Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Fork status
if [[ "${{ needs.fork-check.outputs.is-fork }}" == "true" ]]; then
echo "⚠️ **Fork PR**: This PR is from a fork. Write operations were skipped for security." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
echo "## ✅ Validation Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
# Branch name
if [[ "${{ needs.validate-branch.result }}" == "success" ]]; then
echo "| Branch Name | ✅ Valid |" >> $GITHUB_STEP_SUMMARY
else
echo "| Branch Name | ❌ Invalid |" >> $GITHUB_STEP_SUMMARY
fi
# PR title
if [[ "${{ needs.validate-pr-title.result }}" == "success" ]]; then
echo "| PR Title (Conventional) | ✅ Valid |" >> $GITHUB_STEP_SUMMARY
else
echo "| PR Title (Conventional) | ❌ Invalid |" >> $GITHUB_STEP_SUMMARY
fi
# Linked issue
if [[ "${{ needs.validate-linked-issue.result }}" == "success" ]]; then
echo "| Linked Issue | ✅ Found |" >> $GITHUB_STEP_SUMMARY
else
echo "| Linked Issue | ❌ Missing |" >> $GITHUB_STEP_SUMMARY
fi
# Quality checks
if [[ "${{ needs.quality-checks.result }}" == "success" ]]; then
echo "| Quality Checks | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.quality-checks.result }}" == "skipped" ]]; then
echo "| Quality Checks | ⏭️ Skipped (validation failed) |" >> $GITHUB_STEP_SUMMARY
else
echo "| Quality Checks | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Overall status
if [[ "${{ needs.validate-branch.result }}" == "failure" ]] || \
[[ "${{ needs.validate-pr-title.result }}" == "failure" ]] || \
[[ "${{ needs.validate-linked-issue.result }}" == "failure" ]] || \
[[ "${{ needs.quality-checks.result }}" == "failure" ]]; then
echo "## ❌ PR Validation Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please fix the issues above before this PR can be merged." >> $GITHUB_STEP_SUMMARY
exit 1
else
echo "## ✅ PR Ready for Review" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All validations passed! This PR is ready for code review and merge." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "_PR validation completed at $(date -u '+%Y-%m-%d %H:%M:%S UTC')_" >> $GITHUB_STEP_SUMMARY