ci: add PR hygiene — UNREVIEWED label + Slack review reminders - #12
ci: add PR hygiene — UNREVIEWED label + Slack review reminders#12severindemchuk wants to merge 2 commits into
Conversation
New PRs get the UNREVIEWED label via a reusable workflow (consumers copy templates/pr-label.yml); reviewers remove it manually after reviewing. A weekday-morning cron DMs reviewers/authors on Slack about PRs still carrying the label (nag 1-3, then silent). Dry-run and allowlist phases via Actions variables; PAT now, GitHub App swap planned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds organization-wide PR hygiene automation by applying an UNREVIEWED label to new PRs and sending weekday Slack DM reminders until the label is manually removed.
Changes:
- Introduces a reusable workflow (plus template + local “dogfooding” workflow) to label PRs as
UNREVIEWEDon open/reopen. - Adds a scheduled GitHub Actions workflow that runs a Python script to find
UNREVIEWEDPRs and DM the appropriate people on Slack. - Adds a Slack app manifest to document required Slack bot setup/scopes.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
templates/pr-label.yml |
Template workflow to apply UNREVIEWED label in adopting repos |
scripts/pr_reminder.py |
Implements GitHub search + nag calculation + Slack DM sending |
docs/slack-app-manifest.yml |
Documents Slack app configuration/scopes for the reminder bot |
.github/workflows/reusable-pr-label.yml |
Reusable workflow that ensures the label exists and applies it |
.github/workflows/pr-reminder.yml |
Cron/dispatch workflow wiring env/secrets and running the reminder script |
.github/workflows/pr-label.yml |
Dogfooding workflow to label PRs in this repo |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- pr-reminder.yml declared explicit permissions, which zeroes out any unlisted scope — actions/checkout needs contents: read to work. - pr_reminder.py: bound GitHub/Slack urlopen() calls with a timeout so a hung connection can't stall the whole workflow run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| name: PR label | ||
| on: | ||
| pull_request: | ||
| types: [opened, reopened] |
There was a problem hiding this comment.
So ist es vermutlich einfacher, als erst beim Merge das Label hinzuzufügen. Dann aber bitte auch das Label automatisch entfernen, wenn der PR approved wird: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_review
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels" \ |
There was a problem hiding this comment.
Warum ist das issues? Unterscheidet die REST API nicht zwischen issues und PRs?
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels" \ | ||
| -f "labels[]=UNREVIEWED" |
There was a problem hiding this comment.
Werden so alle bestehenden Labels überschrieben? Oder ist das ein append?
There was a problem hiding this comment.
Ich denke es reicht, wenn man es aus .github/actions kopieren kann. Sonst geraten die Files früher oder später out of sync.
|
|
||
|
|
||
| def search_unreviewed_prs(org, token): | ||
| query = f"org:{org} is:pr is:open label:{LABEL}" |
There was a problem hiding this comment.
Es müsste (is:open OR label:UNREVIEWED) sein. Mir geht es ja vor allem darum, das Reviews nachgeholt werden, wenn (z.B. hotfixes) bereits gemergt wurde.
| GITHUB_API = "https://api.github.com" | ||
| SLACK_API = "https://slack.com/api/chat.postMessage" | ||
| LABEL = "UNREVIEWED" | ||
| MAX_NAGS = 3 |
There was a problem hiding this comment.
Ich würde es glaube ich lieber 1-mal pro Woche machen und dafür kein Limit. Das ein PR ein paar Tage rumliegt, kann schonmal passieren und da soll man nicht unbedingt genervt werden. Es soll eigentlich nur verhindert werden, dass man PRs ganz vergisst.
|
|
||
|
|
||
| def pr_targets(repo, number, author, token): | ||
| """(logins, is_author_fallback) — requested reviewers, else the author.""" |
There was a problem hiding this comment.
Ich würde assignee -> reviewer -> author als Fallback Reihenfolge nehmen. Ich arbeite z.B. so, dass ich den PR dem Author zurück assigne, wenn ich findings habe. Dann macht es keinen Sinn, wenn ich nochmal benachrichtigt werde.
Summary
templates/pr-label.yml) get theUNREVIEWEDlabel automatically. Reviewers remove it manually after reviewing — that removal is the "reviewed" acknowledgment, automation never touches it again.pr-reminder.yml) DMs requested reviewers (or the author, if none requested) on Slack about PRs still carrying the label. Nag 1 the next weekday, daily, hard stop after 3.pr-label.ymlshould label this very PR.Config needed before the reminder cron can run for real (see follow-up)
GH_SLACK_MAP,PR_HYGIENE_DRY_RUN,PR_HYGIENE_ALLOWLISTPR_BOT_PAT,SLACK_BOT_TOKENTest plan
scripts/pr_reminder.pyunit tests (kept local for now, not part of this diff)UNREVIEWEDlabel automatically on open🤖 Generated with Claude Code