Files
nofx/.github/workflows/pr-template-suggester.yml
zbhan c851e1c7cd feat(templates): add intelligent PR template selection system
- Created specialized PR templates for different change types:
  - Backend template for Go/API changes
  - Frontend template for UI/UX changes
  - Documentation template for docs updates
  - General template for mixed changes
- Simplified default template from 270 to 115 lines
- Added GitHub Action for automatic template suggestion based on file types
- Auto-labels PRs with appropriate categories (backend/frontend/documentation)
- Provides friendly suggestions when default template is used

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: tinkle-community <tinklefund@gmail.com>
2025-11-04 15:52:01 -05:00

150 lines
5.8 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: PR Template Suggester
on:
pull_request:
types: [opened, edited, synchronize]
permissions:
pull-requests: write
contents: read
jobs:
suggest-template:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Analyze PR files and suggest template
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
let goFiles = 0;
let jsFiles = 0;
let tsFiles = 0;
let mdFiles = 0;
let otherFiles = 0;
for (const file of files) {
const filename = file.filename.toLowerCase();
if (filename.endsWith('.go')) {
goFiles++;
} else if (filename.endsWith('.js') || filename.endsWith('.jsx')) {
jsFiles++;
} else if (filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.vue')) {
tsFiles++;
} else if (filename.endsWith('.md')) {
mdFiles++;
} else {
otherFiles++;
}
}
const totalFiles = goFiles + jsFiles + tsFiles + mdFiles + otherFiles;
if (totalFiles === 0) {
console.log('No files changed');
return;
}
let suggestedTemplate = null;
let templateEmoji = '';
let templateLabel = '';
if (goFiles / totalFiles > 0.5) {
suggestedTemplate = 'backend';
templateEmoji = '🔧';
templateLabel = 'backend';
} else if ((jsFiles + tsFiles) / totalFiles > 0.5) {
suggestedTemplate = 'frontend';
templateEmoji = '🎨';
templateLabel = 'frontend';
} else if (mdFiles / totalFiles > 0.7) {
suggestedTemplate = 'docs';
templateEmoji = '📝';
templateLabel = 'documentation';
}
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const prBody = pr.body || '';
const usesBackendTemplate = prBody.includes('Pull Request - Backend');
const usesFrontendTemplate = prBody.includes('Pull Request - Frontend');
const usesDocsTemplate = prBody.includes('Pull Request - Documentation');
const usesGeneralTemplate = prBody.includes('Pull Request - General');
const usingDefaultTemplate = !usesBackendTemplate && !usesFrontendTemplate && !usesDocsTemplate && !usesGeneralTemplate;
if (templateLabel) {
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: [templateLabel]
});
console.log(`Added label: ${templateLabel}`);
} catch (error) {
console.log(`Label might not exist, skipping...`);
}
}
if (suggestedTemplate && usingDefaultTemplate) {
const templateUrl = `https://raw.githubusercontent.com/${context.repo.owner}/${context.repo.repo}/dev/.github/PULL_REQUEST_TEMPLATE/${suggestedTemplate}.md`;
let fileStats = [];
if (goFiles > 0) fileStats.push(`🔧 Go files: ${goFiles}`);
if (jsFiles > 0) fileStats.push(`🎨 JavaScript files: ${jsFiles}`);
if (tsFiles > 0) fileStats.push(`🎨 TypeScript files: ${tsFiles}`);
if (mdFiles > 0) fileStats.push(`📝 Markdown files: ${mdFiles}`);
if (otherFiles > 0) fileStats.push(`📦 Other files: ${otherFiles}`);
const fileStatsText = fileStats.map(s => `- ${s}`).join('\n');
const comment = `## ${templateEmoji} 建议使用专用模板 | Suggested Template
您的PR主要包含 **${suggestedTemplate}** 相关的变更。我们建议使用更适合的模板以简化填写。
Your PR primarily contains **${suggestedTemplate}** changes. We suggest using a more suitable template to simplify filling.
**文件统计 | File Statistics**
${fileStatsText}
**推荐模板 | Recommended Template**
\`\`\`
.github/PULL_REQUEST_TEMPLATE/${suggestedTemplate}.md
\`\`\`
**如何使用 | How to use**
1. 编辑PR描述 | Edit PR description
2. 复制 [${suggestedTemplate} 模板内容](${templateUrl}) | Copy [${suggestedTemplate} template content](${templateUrl})
3. 或在创建PR时使用URL参数 | Or use URL parameter when creating PR
\`?template=${suggestedTemplate}.md\`
_这是一个自动建议您可以继续使用当前模板。_
_This is an automated suggestion. You may continue using the current template._`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
console.log(`Suggested ${suggestedTemplate} template`);
} else if (suggestedTemplate && !usingDefaultTemplate) {
console.log(`PR already uses a specific template`);
} else {
console.log('No specific template suggestion needed - mixed changes');
}