-
Notifications
You must be signed in to change notification settings - Fork 724
137 lines (120 loc) · 5.52 KB
/
pr-compliance.yml
File metadata and controls
137 lines (120 loc) · 5.52 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
name: PR Compliance
on:
pull_request:
branches: [main]
types: [opened, edited, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
check-pr:
name: Validate PR
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' && github.actor != 'emdashbot[bot]'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check PR template
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const body = context.payload.pull_request.body || '';
const pr = context.payload.pull_request;
const errors = [];
// Check if the PR body looks like it used the template at all
const hasTemplate = body.includes('## What does this PR do?') || body.includes('## Type of change');
if (!hasTemplate) {
errors.push('This PR does not use the required PR template. Please edit the description to use the [PR template](https://github.com/emdash-cms/emdash/blob/main/.github/PULL_REQUEST_TEMPLATE.md). Copy it into your PR description and fill out all sections.');
} else {
// Must have a description (not just the raw template placeholder)
const descriptionSection = body.match(/## What does this PR do\?\s*\n([\s\S]*?)(?=\n## )/);
const description = descriptionSection?.[1]?.replace(/<!--[\s\S]*?-->/g, '').trim() || '';
if (!description || description === 'Closes #') {
errors.push('Fill out the "What does this PR do?" section with a description of your change.');
}
// Must check at least one type
const typeChecks = [
/- \[x\] Bug fix/i,
/- \[x\] Feature/i,
/- \[x\] Refactor/i,
/- \[x\] Documentation/i,
/- \[x\] Performance/i,
/- \[x\] Tests/i,
/- \[x\] Chore/i,
];
const hasType = typeChecks.some(re => re.test(body));
if (!hasType) {
errors.push('Check at least one "Type of change" checkbox.');
}
// If Feature is checked, require a discussion link
const isFeature = /- \[x\] Feature/i.test(body);
if (isFeature) {
const hasDiscussionLink = /github\.com\/emdash-cms\/emdash\/discussions\/\d+/.test(body);
if (!hasDiscussionLink) {
errors.push('Feature PRs require a link to an approved Discussion (https://github.com/emdash-cms/emdash/discussions/categories/ideas). Open a Discussion first, get approval, then link it in the PR.');
}
}
// Must check the "I have read CONTRIBUTING.md" box
const hasReadContributing = /- \[x\] I have read \[CONTRIBUTING\.md\]/i.test(body);
if (!hasReadContributing) {
errors.push('Check the "I have read CONTRIBUTING.md" checkbox.');
}
}
if (errors.length > 0) {
const message = [
'## PR template validation failed',
'',
'Please fix the following issues by editing your PR description:',
'',
...errors.map(e => `- ${e}`),
'',
'See [CONTRIBUTING.md](https://github.com/emdash-cms/emdash/blob/main/CONTRIBUTING.md) for the full contribution policy.',
].join('\n');
// Leave a comment so the author sees the errors directly
// Find and update existing bot comment, or create a new one
const marker = '<!-- pr-compliance-check -->';
const commentBody = `${marker}\n${message}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
});
const existing = comments.find(c =>
c.user?.login === 'github-actions[bot]' &&
c.body?.includes(marker)
);
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: commentBody,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: commentBody,
});
}
core.setFailed(message);
} else {
// If passing now, remove any previous failure comment
const marker = '<!-- pr-compliance-check -->';
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
});
const existing = comments.find(c =>
c.user?.login === 'github-actions[bot]' &&
c.body?.includes(marker)
);
if (existing) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
});
}
}