Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions .github/workflows/pr-size-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: PR Size Label

on:
pull_request_target:
types:
- opened
- reopened
- synchronize
workflow_dispatch:


permissions:
contents: read
issues: write
pull-requests: write

jobs:
Label:
runs-on: ubuntu-latest

steps:
- name: Label PR by filtered changed lines
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner
const repo = context.repo.repo
const ignoredFilePatterns = [
/(?:^|\/)\.cnb\/.+\.ya?ml$/i,
/(?:^|\/)\.gemini\/.+\.ya?ml$/i,
/(?:^|\/)\.gitee\/.+\.ya?ml$/i,
/(?:^|\/)\.github\/.+\.ya?ml$/i,
/(?:^|\/)\.idea\/.+\.xml$/i,
/(?:^|\/)\.editorconfig$/i,
/(?:^|\/)\.gitattributes$/i,
/(?:^|\/)\.gitignore$/i,
/(?:^|\/).+\.kts$/i,
/(?:^|\/).+\.md$/i,
/(?:^|\/)LICENSE(?:_.*)?$/i,
/(?:^|\/)gradlew(?:\.bat|\.ps1)?$/i,
/(?:^|\/)gradle\/wrapper\/gradle-wrapper\.properties$/i,
/(?:^|\/)lang\/.+\.properties$/i,
/(?:^|\/)l10n\/.+\.properties$/i,
/(?:^|\/)checkstyle\.xml$/i,
/(?:^|\/).+\.(?:png|jpe?g|gif|ico|svg|json|ttf|woff2?|webp|avif|jxl|heic|mp4|webm|mp3|ogg|wav|txt|toml)$/i
]

async function processPR(issue_number) {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: issue_number,
per_page: 100,
})

const countedFiles = []
const ignoredFiles = []
let totalChanges = 0

for (const file of files) {
const ignored = ignoredFilePatterns.some(pattern => pattern.test(file.filename))
if (ignored) {
ignoredFiles.push(file.filename)
continue
}

const fileChanges = file.changes ?? ((file.additions || 0) + (file.deletions || 0))
totalChanges += fileChanges
countedFiles.push(`${file.filename} (${fileChanges})`)
}

let targetLabel
if (totalChanges <= 9) targetLabel = '1+'
else if (totalChanges <= 39) targetLabel = '10+'
else if (totalChanges <= 99) targetLabel = '40+'
else if (totalChanges <= 499) targetLabel = '100+'
else if (totalChanges <= 999) targetLabel = '500+'
else if (totalChanges <= 1999) targetLabel = '1000+'
else if (totalChanges <= 4999) targetLabel = '2000+'
else targetLabel = '5000+'

const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner,
repo,
issue_number,
per_page: 100,
})

const sizeLabels = currentLabels
.map(label => label.name)
.filter(name => /^\d+\+$/.test(name))

for (const name of sizeLabels) {
if (name !== targetLabel) {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number,
name,
}).catch(() => {})
}
}

if (!currentLabels.some(label => label.name === targetLabel)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number,
labels: [targetLabel],
})
}

core.info(`Pull request #${issue_number} has ${totalChanges} counted changed lines, applied ${targetLabel}.`)
core.info(`Ignored files: ${ignoredFiles.length ? ignoredFiles.join(', ') : 'none'}`)
core.info(`Counted files: ${countedFiles.length ? countedFiles.join(', ') : 'none'}`)
}

if (context.eventName === 'workflow_dispatch') {
core.info('Manual trigger detected. Fetching all open pull requests...')

const openPRs = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: 'open',
per_page: 100,
})

const unlabelledPRs = openPRs.filter(pr => {
return !pr.labels.some(label => /^\d+\+$/.test(label.name))
})

core.info(`Found ${openPRs.length} open pull request(s), ${unlabelledPRs.length} of them lack a size label.`)

for (const pr of unlabelledPRs) {
core.info(`----------------------------------------`)
core.info(`Processing unlabelled pull request #${pr.number}: ${pr.title}`)
await processPR(pr.number)
}
} else {
const pr = context.payload.pull_request
if (!pr) {
core.info('No pull request found in payload, skipping.')
return
}
await processPR(pr.number)
}