PR Automation #18
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: PR Automation | |
on: | |
pull_request: | |
types: [opened, synchronize] | |
issues: | |
types: [opened] | |
schedule: | |
# Run weekly for stale checks | |
- cron: '0 0 * * 0' | |
permissions: | |
contents: read | |
pull-requests: write | |
issues: write | |
jobs: | |
auto-label: | |
name: Auto Label | |
runs-on: ubuntu-latest | |
if: github.event_name == 'pull_request' | |
steps: | |
- name: Label based on files changed | |
uses: actions/labeler@v5 | |
with: | |
repo-token: "${{ secrets.GITHUB_TOKEN }}" | |
sync-labels: true | |
- name: Label based on PR size | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const { data: pr } = await github.rest.pulls.get({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
pull_number: context.issue.number | |
}); | |
const additions = pr.additions; | |
const deletions = pr.deletions; | |
const total = additions + deletions; | |
let sizeLabel = ''; | |
if (total < 10) { | |
sizeLabel = 'size/XS'; | |
} else if (total < 50) { | |
sizeLabel = 'size/S'; | |
} else if (total < 250) { | |
sizeLabel = 'size/M'; | |
} else if (total < 500) { | |
sizeLabel = 'size/L'; | |
} else { | |
sizeLabel = 'size/XL'; | |
} | |
// Remove other size labels | |
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number | |
}); | |
const sizeLabels = labels | |
.filter(label => label.name.startsWith('size/')) | |
.filter(label => label.name !== sizeLabel); | |
for (const label of sizeLabels) { | |
await github.rest.issues.removeLabel({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number, | |
name: label.name | |
}).catch(() => {}); // Ignore if label doesn't exist | |
} | |
// Add the appropriate size label | |
await github.rest.issues.addLabels({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number, | |
labels: [sizeLabel] | |
}).catch(() => {}); // Ignore if label already exists | |
# Only welcome if they have 'first-time-contributor' label | |
# This prevents spam on every PR | |
welcome-first-time: | |
name: Welcome First Time Contributors | |
runs-on: ubuntu-latest | |
if: | | |
github.event_name == 'pull_request' && | |
github.event.action == 'opened' && | |
contains(github.event.pull_request.labels.*.name, 'first-time-contributor') | |
steps: | |
- name: Welcome message | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number, | |
body: `Welcome @${context.payload.pull_request.user.login}! 🎉 | |
Thank you for your first contribution! A maintainer will review your PR soon.` | |
}); | |
stale-check: | |
name: Mark Stale Items | |
runs-on: ubuntu-latest | |
if: github.event_name == 'schedule' | |
steps: | |
- name: Mark stale issues and PRs | |
uses: actions/stale@v9 | |
with: | |
repo-token: ${{ secrets.GITHUB_TOKEN }} | |
stale-issue-message: 'This issue has been inactive for 60 days.' | |
stale-pr-message: 'This PR has been inactive for 30 days.' | |
stale-issue-label: 'stale' | |
stale-pr-label: 'stale' | |
days-before-issue-stale: 60 | |
days-before-pr-stale: 30 | |
days-before-close: 14 | |
exempt-pr-labels: 'pinned,security,work-in-progress,full-ci,codeql' | |
exempt-issue-labels: 'pinned,security,bug' | |
operations-per-run: 30 |