76d991c447
Auto Update PR / update-prs (push) Has been cancelled
CI / format-check (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / live-api-tests (push) Has been cancelled
CI / plugin-integration-test (push) Has been cancelled
CI / ollama-integration-test (push) Has been cancelled
CI / test-fork-pr (push) Has been cancelled
131 lines
4.7 KiB
YAML
131 lines
4.7 KiB
YAML
name: Check PR size
|
||
|
||
on:
|
||
pull_request_target:
|
||
types: [opened, synchronize, reopened]
|
||
workflow_dispatch:
|
||
inputs:
|
||
pr_number:
|
||
description: 'PR number to check (optional)'
|
||
required: false
|
||
type: string
|
||
|
||
permissions:
|
||
contents: read
|
||
pull-requests: write
|
||
issues: write
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
size:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Get PR data for manual trigger
|
||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number
|
||
id: get_pr
|
||
uses: actions/github-script@v9
|
||
with:
|
||
result-encoding: string
|
||
script: |
|
||
const { data } = await github.rest.pulls.get({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
pull_number: ${{ github.event.inputs.pr_number }}
|
||
});
|
||
return JSON.stringify(data);
|
||
|
||
- name: Evaluate PR size
|
||
if: github.event_name == 'pull_request_target' || (github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number)
|
||
uses: actions/github-script@v9
|
||
env:
|
||
PR_JSON: ${{ steps.get_pr.outputs.result }}
|
||
with:
|
||
script: |
|
||
const pr = context.payload.pull_request || JSON.parse(process.env.PR_JSON || '{}');
|
||
if (!pr || !pr.number) {
|
||
core.setFailed('Unable to resolve PR data. For workflow_dispatch, pass a valid pr_number.');
|
||
return;
|
||
}
|
||
|
||
// Check for draft PRs and bots
|
||
const isDraft = !!pr.draft;
|
||
const login = pr.user.login;
|
||
const isBot = pr.user.type === 'Bot' || /\[bot\]$/.test(login);
|
||
|
||
if (isDraft || isBot) {
|
||
core.info('Draft or bot PR – skipping size enforcement');
|
||
return;
|
||
}
|
||
|
||
const totalChanges = pr.additions + pr.deletions;
|
||
core.info(`PR contains ${pr.additions} additions and ${pr.deletions} deletions (${totalChanges} total)`);
|
||
|
||
const sizeLabel =
|
||
totalChanges < 50 ? 'size/XS' :
|
||
totalChanges < 150 ? 'size/S' :
|
||
totalChanges < 600 ? 'size/M' :
|
||
totalChanges < 1000 ? 'size/L' : 'size/XL';
|
||
|
||
// Re-fetch labels to avoid acting on stale payload data
|
||
const { data: freshIssue } = await github.rest.issues.get({
|
||
...context.repo,
|
||
issue_number: pr.number
|
||
});
|
||
const currentLabels = (freshIssue.labels || []).map(l => l.name);
|
||
|
||
// Remove old size labels before adding new one
|
||
const allSizeLabels = ['size/XS', 'size/S', 'size/M', 'size/L', 'size/XL'];
|
||
const toRemove = currentLabels.filter(name => allSizeLabels.includes(name) && name !== sizeLabel);
|
||
|
||
for (const name of toRemove) {
|
||
try {
|
||
await github.rest.issues.removeLabel({
|
||
...context.repo,
|
||
issue_number: pr.number,
|
||
name
|
||
});
|
||
} catch (_) {
|
||
// Ignore if already removed
|
||
}
|
||
}
|
||
|
||
await github.rest.issues.addLabels({
|
||
...context.repo,
|
||
issue_number: pr.number,
|
||
labels: [sizeLabel]
|
||
});
|
||
|
||
// Check if PR author is a maintainer
|
||
let authorPerm = 'none';
|
||
try {
|
||
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
username: pr.user.login,
|
||
});
|
||
authorPerm = data.permission || 'none';
|
||
} catch (_) {
|
||
// User might not have any permissions
|
||
}
|
||
|
||
core.info(`Author permission: ${authorPerm}`);
|
||
const isMaintainer = ['admin', 'maintain'].includes(authorPerm); // Stricter maintainer definition
|
||
|
||
// Check for bypass label (using fresh labels)
|
||
const hasBypass = currentLabels.includes('bypass:size-limit');
|
||
|
||
const MAX_LINES = 1000;
|
||
if (totalChanges > MAX_LINES) {
|
||
if (isMaintainer || hasBypass) {
|
||
core.info(`${isMaintainer ? 'Maintainer' : 'Bypass label'} - allowing large PR with ${totalChanges} lines`);
|
||
} else {
|
||
core.setFailed(
|
||
`This PR contains ${totalChanges} lines of changes, which exceeds the maximum of ${MAX_LINES} lines. ` +
|
||
`Please split this into smaller, focused pull requests.`
|
||
);
|
||
}
|
||
}
|