-
Notifications
You must be signed in to change notification settings - Fork 751
179 lines (153 loc) · 7.43 KB
/
Copy pathpr-description-check.yml
File metadata and controls
179 lines (153 loc) · 7.43 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
name: PR Description Validation
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
validate-pr-description:
runs-on: ubuntu-latest
name: Validate PR Description
steps:
- name: Validate PR Description
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const prBody = context.payload.pull_request.body || '';
console.log('Validating PR body...');
// Define required sections from the PR template
const requiredSections = [
{
name: 'Description',
header: '## Description',
placeholder: 'Enter description to help the reviewer understand what\'s the change about...'
},
{
name: 'Changelog',
header: '## Changelog',
placeholder: 'Add a quick message for our users about this change'
},
{
name: 'Additional info',
header: '## Additional info',
placeholder: 'If applicable, add additional info such as link to the bug being fixed by this PR'
}
];
const missingOrEmptySections = [];
const completedSections = [];
// Validate each required section
for (const section of requiredSections) {
console.log(`Checking section: ${section.name}`);
// Check if section header exists
if (!prBody.includes(section.header)) {
missingOrEmptySections.push({
name: section.name,
issue: 'Section is missing completely'
});
continue;
}
// Extract content after the section header
const sectionRegex = new RegExp(
`${section.header.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*(?:<!--[\\s\\S]*?-->)?\\s*([\\s\\S]*?)(?=##|$)`,
'i'
);
const match = prBody.match(sectionRegex);
if (!match) {
missingOrEmptySections.push({
name: section.name,
issue: 'Section header found but no content detected'
});
continue;
}
let content = match[1].trim();
// Remove HTML comments from content for validation
content = content.replace(/<!--[\s\S]*?-->/g, '').trim();
// Check if section is empty or contains only template text
if (!content || content.length === 0) {
missingOrEmptySections.push({
name: section.name,
issue: 'Section is empty - please add content'
});
} else if (content.includes(section.placeholder)) {
missingOrEmptySections.push({
name: section.name,
issue: 'Section contains template placeholder text - please replace with actual content'
});
} else {
completedSections.push(section.name);
console.log(`✅ ${section.name} section is properly filled`);
}
}
// Create feedback message
let commentBody = '';
let statusIcon = '';
let statusTitle = '';
if (missingOrEmptySections.length === 0) {
// All sections are complete
statusIcon = '✅';
statusTitle = 'PR Description Validation Passed';
commentBody += `## ${statusIcon} ${statusTitle}\n\n`;
commentBody += 'All required sections are properly filled out:\n\n';
completedSections.forEach(section => {
commentBody += `- ✅ **${section}**\n`;
});
commentBody += '\nYour PR is good for review! 🚀\n';
} else {
// Some sections are missing or incomplete
statusIcon = '❌';
statusTitle = 'PR Description Validation Failed';
commentBody += `## ${statusIcon} ${statusTitle}\n\n`;
commentBody += 'The following required sections need attention:\n\n';
missingOrEmptySections.forEach(section => {
commentBody += `- ❌ **${section.name}**: ${section.issue}\n`;
});
if (completedSections.length > 0) {
commentBody += '\n**Completed sections:**\n';
completedSections.forEach(section => {
commentBody += `- ✅ **${section}**\n`;
});
}
commentBody += '\n---\n\n';
commentBody += '### Required Sections:\n';
commentBody += '- [ ] **Description** - Explain what changes are being made and why\n';
commentBody += '- [ ] **Changelog** - User-facing summary, this will show in the release notes (include component names, relevant props, and general purpose)\n';
commentBody += '- [ ] **Additional info** - Links to related issues, Jira tickets\n\n';
commentBody += 'Please update your PR description to include all required sections with meaningful content.\n';
}
commentBody += '\n---\n';
commentBody += '_This validation ensures all sections from the [PR template](/wix/react-native-ui-lib/blob/master/.github/pull_request_template.md) are properly filled._';
// Find existing validation comment to update it
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('PR Description Validation')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
console.log('Updated existing validation comment');
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: commentBody
});
console.log('Created new validation comment');
}
// Set workflow status
if (missingOrEmptySections.length > 0) {
const failureMessage = `Missing or incomplete sections: ${missingOrEmptySections.map(s => s.name).join(', ')}`;
core.setFailed(failureMessage);
console.log(`❌ Validation failed: ${failureMessage}`);
} else {
console.log('✅ PR description validation passed - all sections complete!');
}