GitHub Actions Issue and PR Automation

Busy repositories collect dozens of issues and pull requests every week. Sorting, labeling, and greeting each one by hand drains time from real development. Workflows react to these events instantly and handle the routine work.

Issue and PR Automation in Simple Words

Picture a receptionist at a clinic. Each visitor gets a greeting, a numbered ticket, and a pointer to the right department. Workflows play the receptionist for your repository. Every new issue or pull request receives a welcome, a label, and a route to the right person.

Events That Start Workflows

EventCommon TypesTypical Use
issuesopened, labeled, closedWelcome and triage
issue_commentcreatedRespond to commands
pull_requestopened, synchronize, ready_for_reviewLabeling and checks
schedulecron timerClose stale items

Automation Flow

New issue opened
       |
       v
+---------------+     +-----------------+     +------------------+
| Workflow runs | --> | Add label       | --> | Post welcome     |
|               |     | "needs-triage"  |     | comment          |
+---------------+     +-----------------+     +------------------+

Welcome Comment With github-script

The actions/github-script action runs short JavaScript snippets with a ready-made GitHub client.

name: Welcome

on:
  issues:
    types: [ opened ]

permissions:
  issues: write

jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - 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: "Thanks for opening this issue. A maintainer will review it soon."
            });
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              labels: ["needs-triage"]
            });

Label Pull Requests by File Path

The actions/labeler action reads a rules file and applies labels based on changed files.

# .github/labeler.yml
documentation:
  - changed-files:
      - any-glob-to-any-file: "docs/**"

frontend:
  - changed-files:
      - any-glob-to-any-file: "src/ui/**"
name: Label PRs

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v5
PR changes docs/intro.md   -->  label: documentation
PR changes src/ui/menu.js  -->  label: frontend

Close Stale Items

The actions/stale action warns and then closes items with no recent activity. A scheduled trigger runs it daily.

name: Stale Cleanup

on:
  schedule:
    - cron: "30 1 * * *"

permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          days-before-stale: 60
          days-before-close: 14
          stale-issue-message: "This issue has had no activity for 60 days."

Permissions Matter

Each example grants only the scopes it needs. Commenting on an issue needs issues: write. Labeling a pull request needs pull-requests: write. Missing scopes cause the error "Resource not accessible by integration".

Safety With Forked Pull Requests

The pull_request event gives forked contributions a read-only token, so labeling may fail on fork PRs. The pull_request_target event gives write access but runs in the context of your base branch. Never check out and run code from the fork inside a pull_request_target workflow. Untrusted code with a write token can damage your repository.

Ideas to Try

  • Assign new bug reports to a rotating maintainer.
  • Add a checklist comment to every pull request.
  • Move issues to a project board when they receive a label.
  • Remind authors to update the changelog when source files change.

Start with one small automation, watch it for a week, and add more after the team trusts it.