Skip to content

All-in-one Install YAML Manifests for GitOps #72

All-in-one Install YAML Manifests for GitOps

All-in-one Install YAML Manifests for GitOps #72

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