mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-09 22:10:57 +08:00
The pr-template-suggester workflow was triggered on opened/edited/synchronize events and forcefully replaced the PR body with a template when body < 100 chars. This caused user-written descriptions to be overwritten. Replace with a lightweight labeler (OpenClaw-style) that: - Only adds labels (backend/frontend/docs, size: XS/S/M/L/XL) - Never modifies the PR body - Simplified unified PR template at .github/pull_request_template.md
93 lines
3.6 KiB
YAML
93 lines
3.6 KiB
YAML
name: PR Labeler
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened]
|
|
|
|
permissions:
|
|
pull-requests: write
|
|
contents: read
|
|
|
|
jobs:
|
|
label-pr:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Analyze PR and apply labels
|
|
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,
|
|
per_page: 100,
|
|
});
|
|
|
|
let goFiles = 0, jsFiles = 0, tsFiles = 0, mdFiles = 0, otherFiles = 0;
|
|
let additions = 0, deletions = 0;
|
|
|
|
for (const file of files) {
|
|
const name = file.filename.toLowerCase();
|
|
additions += file.additions || 0;
|
|
deletions += file.deletions || 0;
|
|
if (name.endsWith('.go')) goFiles++;
|
|
else if (name.endsWith('.js') || name.endsWith('.jsx')) jsFiles++;
|
|
else if (name.endsWith('.ts') || name.endsWith('.tsx') || name.endsWith('.vue')) tsFiles++;
|
|
else if (name.endsWith('.md')) mdFiles++;
|
|
else otherFiles++;
|
|
}
|
|
|
|
const totalFiles = goFiles + jsFiles + tsFiles + mdFiles + otherFiles;
|
|
if (totalFiles === 0) return;
|
|
|
|
// --- Scope label ---
|
|
const labels = [];
|
|
if (goFiles / totalFiles > 0.5) labels.push('backend');
|
|
else if ((jsFiles + tsFiles) / totalFiles > 0.5) labels.push('frontend');
|
|
else if (mdFiles / totalFiles > 0.7) labels.push('documentation');
|
|
else labels.push('fullstack');
|
|
|
|
// --- Size label (like OpenClaw) ---
|
|
const totalChanged = additions + deletions;
|
|
const sizeLabels = ['size: XS', 'size: S', 'size: M', 'size: L', 'size: XL'];
|
|
let sizeLabel = 'size: XL';
|
|
if (totalChanged < 50) sizeLabel = 'size: XS';
|
|
else if (totalChanged < 200) sizeLabel = 'size: S';
|
|
else if (totalChanged < 500) sizeLabel = 'size: M';
|
|
else if (totalChanged < 1000) sizeLabel = 'size: L';
|
|
labels.push(sizeLabel);
|
|
|
|
// Ensure size labels exist
|
|
for (const sl of sizeLabels) {
|
|
try {
|
|
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: sl });
|
|
} catch (e) {
|
|
if (e.status === 404) {
|
|
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: sl, color: 'b76e79' });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove stale size labels
|
|
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
|
|
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
|
|
});
|
|
for (const cl of currentLabels) {
|
|
if (sizeLabels.includes(cl.name) && cl.name !== sizeLabel) {
|
|
await github.rest.issues.removeLabel({
|
|
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, name: cl.name,
|
|
}).catch(() => {});
|
|
}
|
|
}
|
|
|
|
// Apply labels
|
|
await github.rest.issues.addLabels({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.issue.number,
|
|
labels: labels,
|
|
});
|
|
|
|
console.log(`Applied labels: ${labels.join(', ')} (${totalChanged} lines changed)`);
|