Every developer knows the drill. You open a PR, manually slap on some labels, ping someone in Slack for a review, then spend 10 minutes writing release notes that nobody reads. Multiply that by 50 PRs a week across three client projects, and suddenly you've lost half a day to busywork.
GitHub Actions can handle most of this automatically. Here are 7 free actions that eliminate the repetitive tasks eating into your actual development time.
1. Auto-Label PRs by Files Changed
Action: actions/labeler
Instead of manually tagging every PR as "frontend" or "api" or "docs," this action reads what files changed and applies labels automatically.
The setup:
name: "Pull Request Labeler"
on:
pull_request_target:
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
Then create .github/labeler.yml:
frontend:
- changed-files:
- any-glob-to-any-file: 'src/frontend/**/*'
api:
- changed-files:
- any-glob-to-any-file: 'src/api/**'
documentation:
- changed-files:
- any-glob-to-any-file: '**/*.md'
Now every PR touching frontend code gets labeled frontend automatically. Filter your PR list by label and instantly see all API changes across the week.
Watch out for: This uses pull_request_target for security reasons. If you're accepting PRs from forks, be careful about what the workflow can access.
2. Flag Oversized PRs Before They Become a Problem
Action: codelytv/pr-size-labeler
Large PRs kill velocity. They take forever to review, they're more likely to have bugs, and reviewers start skimming instead of reading. This action labels PRs by size (XS through XL) so you can spot trouble early.
The setup:
name: PR Size Labeler
on: [pull_request]
jobs:
labeler:
permissions:
pull-requests: write
contents: read
runs-on: ubuntu-latest
steps:
- uses: codelytv/pr-size-labeler@v1
with:
xs_max_size: '10'
s_max_size: '100'
m_max_size: '500'
l_max_size: '1000'
fail_if_xl: 'false'
message_if_xl: >
This PR exceeds 1000 lines. Consider breaking it into smaller chunks for easier review.
files_to_ignore: |
package-lock.json
yarn.lock
pnpm-lock.yaml
When someone opens a 1,500-line PR, they get a gentle nudge to split it up. When you're planning reviews for the day, size/xl tells you that one needs an hour, not 15 minutes.
Watch out for: Lock files inflate the count. Always add them to files_to_ignore.
3. Auto-Close Stale PRs and Issues
Action: actions/stale
Every repo accumulates zombie PRs. Someone started a feature, got pulled to another project, and now there's a 6-month-old PR rotting in the queue. This action warns authors after a period of inactivity, then closes the PR if nothing happens.
The setup:
name: 'Close Stale PRs'
on:
schedule:
- cron: '30 9 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
stale-pr-message: |
This PR has been inactive for 14 days.
If you're still working on it, drop a comment and I'll leave it alone.
stale-pr-label: 'stale'
days-before-pr-stale: 14
days-before-pr-close: 7
exempt-pr-labels: 'work-in-progress,blocked,waiting-on-external'
exempt-draft-pr: true
The action runs daily at 9:30 AM. After 14 days of no activity, it adds a stale label and posts a comment. Seven days later, if still no response, it closes the PR. Draft PRs and anything labeled work-in-progress are exempt.
Watch out for: The default operations-per-run is 30. If you have a backlog of stale items, bump this to 100+ or it'll take multiple days to process everything.
4. Enforce Commit Message Conventions
Action: amannn/action-semantic-pull-request
Commit messages like "fix stuff" or "WIP" make git history useless. This action enforces conventional commit format (feat:, fix:, docs:, etc.) on PR titles, which matters if you squash-merge.
The setup:
name: 'Validate PR Title'
on:
pull_request_target:
types: [opened, edited, synchronize]
jobs:
main:
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
test
chore
requireScope: false
wip: true
Now PRs must be titled like feat: add user authentication or fix: resolve memory leak in dashboard. The wip: true setting lets people bypass this with "WIP:" prefix while they're still working.
This is used by Electron, Vite, and Vercel. Once you have consistent PR titles, automatic changelog generation becomes possible (see #6).
Watch out for: This validates PR titles, not individual commits. Works best when your repo uses GitHub's squash-merge setting.
5. Lint Everything With One Action
Action: super-linter/super-linter
If your team touches JavaScript, Python, YAML, Markdown, Dockerfiles, and Terraform across different projects, maintaining separate linters is a nightmare. Super-Linter runs 50+ linters in a single action.
The setup:
name: Lint Code Base
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
permissions:
contents: read
statuses: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: super-linter/super-linter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
Setting VALIDATE_ALL_CODEBASE: false means it only lints changed files, which keeps PR checks fast. It auto-detects languages and applies appropriate linters—ESLint for JS, Pylint for Python, markdownlint for docs.
Watch out for: The Docker image is around 2GB. First run takes a while, but subsequent runs use cache. You can also disable specific linters with environment variables like VALIDATE_PYTHON: false.
6. Generate Release Notes Automatically
Action: mikepenz/release-changelog-builder-action
Writing release notes by hand means scrolling through every merged PR, categorizing changes, and formatting markdown. This action generates changelogs automatically from your PR labels and titles.
The setup:
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Changelog
id: changelog
uses: mikepenz/release-changelog-builder-action@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.changelog }}
When you push a tag like v1.2.0, this action:
- Finds all PRs merged since the last tag
- Groups them by label (features, fixes, docs)
- Generates formatted release notes
- Creates a GitHub release with that content
This is where the earlier actions pay off. If you've been auto-labeling PRs and enforcing commit conventions, your release notes practically write themselves.
Watch out for: Requires consistent PR labeling. Combine with actions/labeler for best results.
7. Send Slack Notifications for Key Events
Action: rtCamp/action-slack-notify
Instead of checking GitHub Actions status manually and posting updates to Slack, let the workflow do it.
The setup:
name: Deploy Notification
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Your deploy steps here...
- name: Notify Success
if: success()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CHANNEL: deployments
SLACK_COLOR: 'good'
SLACK_TITLE: 'Deploy Complete'
SLACK_MESSAGE: 'Production deploy successful :rocket:'
- name: Notify Failure
if: failure()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CHANNEL: deployments
SLACK_COLOR: 'danger'
SLACK_TITLE: 'Deploy Failed'
SLACK_MESSAGE: 'Production deploy failed. Check GitHub Actions.'
Deploys, test failures, and build completions show up in Slack where the team already lives. No more "did the deploy go through?" questions in standup.
Watch out for: You'll need a Slack Incoming Webhook (free, takes 2 minutes to set up). Be selective about what triggers notifications—too many and people start ignoring them.
Putting It Together
These actions work best in combination. Here's what a typical workflow looks like:
- Developer opens a PR
-
actions/labelertags it by files changed -
codelytv/pr-size-labelerflags if it's too large -
amannn/action-semantic-pull-requestvalidates the PR title -
super-linter/super-linterchecks code quality - After merge,
actions/stalekeeps the backlog clean - On release,
mikepenz/release-changelog-builder-actiongenerates notes -
rtCamp/action-slack-notifytells the team it's done
Each action takes 5-10 minutes to set up. Combined, they eliminate hours of weekly busywork—time better spent actually building things.
What GitHub Actions have saved your team the most time? Always looking for more to add to our workflows.
If you're tired of juggling GitHub, Slack, spreadsheets, and three other tools to manage agency projects, Teamcamp puts it all in one place. Project management, time tracking, client collaboration, and invoicing.
Top comments (0)