-
-
Notifications
You must be signed in to change notification settings - Fork 14
238 lines (200 loc) · 9.12 KB
/
Copy pathproposal-numbering.yml
File metadata and controls
238 lines (200 loc) · 9.12 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
#
# Copyright Kroxylicious Authors.
#
# Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
#
# Automatically checks that proposal files follow the PR-number naming convention (e.g., proposals/092-my-proposal.md for PR #92).
# When a proposal file doesn't match its PR number, this workflow updates the PR description with instructions to rename it.
# Once the author fixes the naming, the warning is automatically removed from the PR description.
name: Check Proposal Numbering
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
pull-requests: write
contents: read
jobs:
check-numbering:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Check proposal file numbering
id: check
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Find all .md files directly in proposals directory (not subdirectories)
# Include both Added (A) and Renamed (R) files
ALL_PROPOSAL_FILES=$(git diff --name-only --diff-filter=AR origin/${{ github.base_ref }}...HEAD | grep '^proposals/[^/]*\.md$' || true)
# Filter out non-proposal files (README.md and template)
PROPOSAL_FILES=""
for file in $ALL_PROPOSAL_FILES; do
# Skip README.md and the template
if [[ "$file" == "proposals/README.md" ]] || [[ "$file" == "proposals/000-template.md" ]]; then
continue
fi
PROPOSAL_FILES="$PROPOSAL_FILES $file"
done
if [ -z "$PROPOSAL_FILES" ]; then
echo "No proposal files found in this PR"
echo "has_proposal=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "has_proposal=true" >> $GITHUB_OUTPUT
# Expected filename pattern: proposals/0{PR_NUMBER}-*.md
EXPECTED_PREFIX=$(printf "proposals/%03d-" $PR_NUMBER)
INCORRECT_FILES=""
INCORRECT_TITLES=""
for file in $PROPOSAL_FILES; do
# Check filename
if [[ ! "$file" =~ ^${EXPECTED_PREFIX} ]]; then
if [ -z "$INCORRECT_FILES" ]; then
INCORRECT_FILES="$file"
else
INCORRECT_FILES="$INCORRECT_FILES,$file"
fi
fi
# Check title format (should be: # <PR_NUMBER> - <Title>)
EXPECTED_TITLE_PREFIX="# $PR_NUMBER -"
if ! grep -q "^${EXPECTED_TITLE_PREFIX}" "$file"; then
if [ -z "$INCORRECT_TITLES" ]; then
INCORRECT_TITLES="$file"
else
INCORRECT_TITLES="$INCORRECT_TITLES,$file"
fi
fi
done
if [ -n "$INCORRECT_FILES" ] || [ -n "$INCORRECT_TITLES" ]; then
echo "incorrect=true" >> $GITHUB_OUTPUT
echo "files=$INCORRECT_FILES" >> $GITHUB_OUTPUT
echo "titles=$INCORRECT_TITLES" >> $GITHUB_OUTPUT
echo "expected_prefix=$EXPECTED_PREFIX" >> $GITHUB_OUTPUT
else
echo "incorrect=false" >> $GITHUB_OUTPUT
fi
- name: Update PR description if numbering is incorrect
if: steps.check.outputs.incorrect == 'true'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const prNumber = context.payload.pull_request.number;
const expectedPrefix = '${{ steps.check.outputs.expected_prefix }}';
const incorrectFilesRaw = '${{ steps.check.outputs.files }}';
const incorrectTitlesRaw = '${{ steps.check.outputs.titles }}';
// Get all files that need fixing (union of incorrect filenames and incorrect titles)
const filesNeedingFix = new Set();
if (incorrectFilesRaw) {
incorrectFilesRaw.split(',').forEach(f => filesNeedingFix.add(f.trim()));
}
if (incorrectTitlesRaw) {
incorrectTitlesRaw.split(',').forEach(f => filesNeedingFix.add(f.trim()));
}
const warnings = [];
if (filesNeedingFix.size > 0) {
const files = Array.from(filesNeedingFix);
const filesList = files.map(f => `- <code>${f}</code>`).join('\n');
warnings.push(
'**Issues found:**',
'',
filesList,
''
);
// For each file, generate specific fix commands
files.forEach(file => {
const basename = file.replace('proposals/', '');
// Strip leading numbers or placeholder prefixes to get the descriptive part
const descriptive = basename.replace(/^[0-9]{3}-/, '').replace(/^(xxx|nnn|000)-/, '');
const expectedFilename = `proposals/${String(prNumber).padStart(3, '0')}-${descriptive}`;
const needsRename = file !== expectedFilename;
const needsTitleUpdate = incorrectTitlesRaw && incorrectTitlesRaw.includes(file);
if (needsRename || needsTitleUpdate) {
warnings.push(
`**Fix for <code>${file}</code>:**`,
'',
'```bash'
);
if (needsRename) {
warnings.push(`git mv ${file} ${expectedFilename}`);
}
if (needsTitleUpdate) {
const targetFile = needsRename ? expectedFilename : file;
warnings.push(
`# Update title: remove any old number prefix and add PR number`,
`sed -i.bak '0,/^# /{s/^# \\([0-9]\\{3\\}\\|xxx\\|nnn\\|000\\) - /# ${prNumber} - /; t; s/^# /# ${prNumber} - /}' ${targetFile} && rm ${targetFile}.bak`,
`git add ${targetFile}`
);
}
warnings.push(
`git commit -m "Rename proposal to use PR number ${prNumber}"`,
'git push',
'```',
''
);
}
});
warnings.push(
'**Note:** The commands above can be copied and pasted directly into your terminal. The sed command will automatically preserve your title text.',
''
);
}
const warningSection = [
'---',
'',
'## ⚠️ Proposal Numbering',
'',
'This PR contains proposal files that don\'t follow the expected format:',
'',
...warnings,
'',
'See [proposals/README.md](https://github.com/kroxylicious/design/blob/main/proposals/README.md) for the complete workflow.',
'',
'<!-- proposal-numbering-check -->'
].join('\n');
// Get current PR
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
let currentBody = pr.body || '';
// Remove existing warning section if present
const markerRegex = /---\s*\n\s*##\s*⚠️\s*Proposal (File )?Numbering[\s\S]*?<!-- proposal-numbering-check -->/;
currentBody = currentBody.replace(markerRegex, '').trim();
// Append new warning section
const newBody = currentBody + '\n' + warningSection;
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
body: newBody
});
- name: Remove warning from PR description if numbering is correct
if: steps.check.outputs.has_proposal == 'true' && steps.check.outputs.incorrect == 'false'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const prNumber = context.payload.pull_request.number;
// Get current PR
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
let currentBody = pr.body || '';
// Check if warning section exists
const markerRegex = /---\s*\n\s*##\s*⚠️\s*Proposal (File )?Numbering[\s\S]*?<!-- proposal-numbering-check -->/;
if (markerRegex.test(currentBody)) {
// Remove warning section
const newBody = currentBody.replace(markerRegex, '').trim();
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
body: newBody
});
console.log('Removed numbering warning from PR description - file is now correctly named');
}