name: '📏 Gemini PR Size Labeler'
on:
pull_request_target:
types: ['opened', 'reopened', 'synchronize']
permissions:
pull-requests: 'write'
contents: 'read'
concurrency:
group: '${{ github.workflow }}-${{ github.event.pull_request.number }}'
cancel-in-progress: true
jobs:
label-pr:
timeout-minutes: 5
runs-on: 'ubuntu-latest'
steps:
- name: 'Get PR diff stats'
id: 'diff_stats'
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # v8.0.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
});
const additions = pr.additions || 0;
const deletions = pr.deletions || 0;
const totalChanges = additions + deletions;
const filesChanged = pr.changed_files || 0;
core.setOutput('total_changes', totalChanges);
core.setOutput('files_changed', filesChanged);
core.setOutput('additions', additions);
core.setOutput('deletions', deletions);
core.info(`PR #${pr.number}: +${additions} -${deletions} (${totalChanges} total) in ${filesChanged} files`);
- name: 'Determine size label'
id: 'size_label'
run: |
TOTAL=${{ steps.diff_stats.outputs.total_changes }}
if [ $TOTAL -lt 10 ]; then
echo "size_label=size/xs" >> $GITHUB_OUTPUT
elif [ $TOTAL -lt 50 ]; then
echo "size_label=size/s" >> $GITHUB_OUTPUT
elif [ $TOTAL -lt 200 ]; then
echo "size_label=size/m" >> $GITHUB_OUTPUT
elif [ $TOTAL -lt 1000 ]; then
echo "size_label=size/l" >> $GITHUB_OUTPUT
else
echo "size_label=size/xl" >> $GITHUB_OUTPUT
fi
- name: 'Apply size label'
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # v8.0.0
env:
SIZE_LABEL: '${{ steps.size_label.outputs.size_label }}'
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
script: |
const prNumber = context.payload.pull_request.number;
const newLabel = process.env.SIZE_LABEL;
// Get current labels
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber
});
// Remove old size labels
const sizeLabels = ['size/xs', 'size/s', 'size/m', 'size/l', 'size/xl'];
for (const label of currentLabels) {
if (sizeLabels.includes(label.name) && label.name !== newLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: label.name
});
core.info(`Removed old label: ${label.name}`);
}
}
// Add new size label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: [newLabel]
});
core.info(`Applied label: ${newLabel}`);
- name: 'Comment on large PRs'
if: steps.size_label.outputs.size_label == 'size/xl'
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # v8.0.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
script: |
const prNumber = context.payload.pull_request.number;
// Check if we already commented
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber
});
const botComment = comments.find(c =>
c.user && c.user.login && c.user.login.includes('[bot]') &&
c.body.includes('This pull request is quite large')
);
if (!botComment) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `## 📏 Large PR Detected
This pull request is quite large (1000+ lines changed), which can make reviewing challenging.
**Suggestions:**
- Consider breaking this into smaller, focused PRs
- Separate refactoring from new features
- Split bug fixes from feature additions
This helps reviewers provide better feedback and speeds up the merge process. Thank you! 🙏`
});
}