Add retry mechanism for transient API errors (503, 429, timeouts) #248
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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@v7 | |
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@v7 | |
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.` | |
); | |
} | |
} |