Skip to content

Improve proposal numbering workflow with exact commands to name proposal file given in feedback added to the description etc #4

Improve proposal numbering workflow with exact commands to name proposal file given in feedback added to the description etc

Improve proposal numbering workflow with exact commands to name proposal file given in feedback added to the description etc #4

#
# 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:
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
- 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');
}