ci: add PR hygiene — UNREVIEWED label + Slack review reminders - #12
ci: add PR hygiene — UNREVIEWED label + Slack review reminders#12severindemchuk wants to merge 3 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
There was a problem hiding this comment.
Das war bewusst so entschieden, dass das entfernen vom Label impliziert das man fertig ist mit dem Review. Ausserdem kann man gemergte PRs nicht mehr approven nur kommentieren.
| 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?
There was a problem hiding this comment.
-> GitHub's REST API has no PR-specific labels endpoint — labels only live under /issues/{number}/labels since a PR is an issue under the hood (gh pr edit --add-label hits the same endpoint internally). So issues here is required, not a mix-up.
| 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.
Ist ein append, kein replace
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.
There was a problem hiding this comment.
Aus welchem .github/actions meinst du?
…rity Laila's PR #12 review feedback: search query dropped is:open only, missing merged-but-unreviewed PRs (e.g. hotfixes reviewed after the fact) — now matches is:merged OR is:open. Cadence changed from daily/capped-at-3 to weekly/uncapped, since the goal is "don't forget entirely" not urgency escalation. pr_targets() now checks assignees first — reassigning a PR to the author after requesting changes should stop notifying the reviewer, since requested_reviewers isn't cleared by submitting a review. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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