A GitHub Actions workflow that automatically keeps pull requests up-to-date when auto-merge is enabled and the base branch changes.
When your repository uses:
- Required linear history (no merge commits in main/develop)
- Required status checks before merge (CI must pass)
- Auto-merge on pull requests (to automatically merge when ready)
You encounter this workflow issue:
- PR #1 merges to
main→ Makes PR #2, #3, #4 out-of-date - You must manually click "Update branch" on PR #2 → Wait for CI
- PR #2 merges → Makes PR #3, #4 out-of-date again
- Repeat for every PR in the chain
This is tedious and time-consuming for teams working with stacked PRs or release trains.
This workflow automates the entire process:
- Detects when commits are pushed to your base branch (e.g.,
main,develop) - Finds all open PRs targeting that branch with auto-merge enabled
- Automatically merges the base branch into those PR branches
- Triggers CI on the updated branches
- Auto-merge completes once CI passes
- ✅ Fully automated: No manual "Update branch" clicks needed
- ✅ Smart detection: Only updates PRs with auto-merge enabled
- ✅ Conflict handling: Gracefully skips PRs with merge conflicts
- ✅ Multi-branch support: Configure for
main,develop,staging, etc. - ✅ Proper CI triggering: Uses GitHub App tokens to ensure CI runs on updated branches
GitHub offers a built-in Merge Queue feature that provides a more robust solution for managing PR merges.
How it works:
- PRs are added to a queue when approved and checks pass
- GitHub creates temporary "merge group" branches that combine queued changes
- Tests run on these merge groups to catch integration issues before merging
- If tests pass, changes are merged to the base branch atomically
- If tests fail, the problematic PR is removed and the queue continues
Benefits:
- Catches integration bugs that only appear when multiple PRs are combined
- Prevents "broken main" scenarios where individually passing PRs break when merged together
- Managed by GitHub with automatic retry logic
- Built-in merge conflict handling
Availability:
This workflow provides automatic PR updates for repositories without Enterprise Cloud access.
When to use:
- You don't have access to GitHub Enterprise Cloud
- You want individual PR merges with preserved history
- You need customizable behavior (specific branches, merge strategies, etc.)
- Your CI catches integration issues reliably
Trade-offs:
- Does not test PRs together before merging (integration issues may slip through)
- Each PR merges sequentially rather than being validated as a batch
- Requires manual workflow setup and maintenance
- Repository admin access (to add secrets/variables)
GitHub Apps are the recommended approach for all users:
- Fine-grained permissions: Only grant exactly what's needed (Contents write, PRs read)
- Auto-expiring tokens: Tokens expire after 1 hour (more secure than long-lived PATs)
- Better audit trail: Actions are clearly attributed to the app
- Not tied to user accounts: For organizations, survives employee turnover
-
Go to GitHub App settings:
- Organization:
https://github.com/organizations/YOUR-ORG/settings/apps - Personal account:
https://github.com/settings/apps
- Organization:
-
Click "New GitHub App"
Basic settings:
- Name:
PR Auto-Update Bot(must be unique, adjust if taken) - Homepage URL: Your organization's GitHub URL
- Webhook: Uncheck "Active" (not needed)
Repository permissions:
- Contents:
Read and write - Pull requests:
Read-only
Installation:
- Where can this GitHub App be installed?:
Only on this account
Click "Create GitHub App"
After creating the app:
-
Note the App ID at the top of the page:
App ID: 1234567 -
Generate a private key:
- Scroll to "Private keys" section
- Click "Generate a private key"
- A
.pemfile will download - Save this file securely (can only download once)
- In app settings, click "Install App" (left sidebar)
- Click "Install" next to your organization/account
- Select repositories (all or specific ones)
- Click "Install"
Go to: https://github.com/YOUR-ORG/YOUR-REPO/settings/secrets/actions
Add variable:
- Click "Variables" tab → "New repository variable"
- Name:
APP_ID - Value: Your App ID from Step 3 (e.g.,
1234567)
Add secret:
- Click "Secrets" tab → "New repository secret"
- Name:
APP_PRIVATE_KEY - Value: Entire contents of
.pemfile:-----BEGIN RSA PRIVATE KEY----- (all the key data) -----END RSA PRIVATE KEY-----
Copy .github/workflows/auto-update-prs.yml from this repository to yours.
Edit the trigger to match your base branch:
on:
push:
branches:
- main # Change to your base branch (develop, staging, etc.)Commit and push. The workflow is now active!
If you prefer not to create a GitHub App:
- Go to:
https://github.com/settings/tokens?type=beta - Click "Generate new token"
- Give it a name: "PR Auto-Update"
- Select repositories (all or specific)
- Permissions:
- Contents: Read and write
- Pull requests: Read-only
- Click "Generate token" and copy it
- Go to:
https://github.com/YOUR-USERNAME/YOUR-REPO/settings/secrets/actions - Click "New repository secret"
- Name:
PAT_TOKEN - Value: Your PAT from Step 1
In .github/workflows/auto-update-prs.yml, replace the "Generate GitHub App token" step with:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}And update the environment variable:
- name: Update PRs with auto-merge enabled
env:
GH_TOKEN: ${{ secrets.PAT_TOKEN }} # Changed from app token
BASE_BRANCH: ${{ github.ref_name }}Via GitHub CLI:
gh pr merge <PR_NUMBER> --auto --squashVia GitHub UI:
- Open the pull request
- Click "Enable auto-merge" button
- Select your merge method (squash/merge/rebase)
When you have multiple PRs with auto-merge enabled:
PR #1: Merge to main (manual)
↓ (workflow triggers)
PR #2-5: Branches updated automatically
↓ (CI runs)
PR #2: CI passes → Auto-merges
↓ (workflow triggers)
PR #3-5: Branches updated automatically
↓ (CI runs)
PR #3: CI passes → Auto-merges
↓ (continues...)
-
Create test PRs:
git checkout -b test-pr-1 echo "test" > test1.txt git add . && git commit -m "Test 1" git push -u origin test-pr-1 gh pr create --base main --title "Test PR 1" gh pr merge $(gh pr view --json number -q .number) --auto --squash
-
Repeat for 2-3 more PRs
-
Trigger the cascade:
- Merge the first PR, or
- Push a commit directly to
main
-
Verify:
- Check Actions tab for workflow runs
- Watch PRs update and merge automatically
- Confirm CI triggers on updated branches
To trigger on multiple branches (e.g., main, develop, staging):
on:
push:
branches:
- main
- develop
- stagingThe workflow automatically adapts to whichever branch triggered it - no other changes needed!
Use glob patterns for release branches:
on:
push:
branches:
- main
- 'release/**'The included ci.yml workflow is a simple example with a 10-second sleep. Replace it with your actual CI:
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: |
npm install
npm testCause: GitHub App lacks required permissions.
Fix:
- Go to app settings → Permissions & events
- Verify Contents = "Read and write"
- Reinstall app on repository
Cause: Private key or PAT is incorrect.
Fix:
- Verify entire key copied (including
-----BEGIN/END-----markers) - Check for extra spaces or line breaks
- Regenerate key/token if needed
Cause: Using GITHUB_TOKEN instead of app token or PAT.
Fix: The workflow in this repo already uses the correct token. Verify:
- For GitHub App: Token generated via
actions/create-github-app-token@v1 - For PAT: Token set in checkout action and GH_TOKEN env var
Cause: Missing credentials or workflow disabled.
Fix:
- Verify secrets/variables exist in repository settings
- Check workflow is enabled:
gh workflow list - Verify push to base branch triggers the workflow
Cause: Auto-merge not enabled on PR, or CI failing.
Fix:
- Verify auto-merge is enabled:
gh pr view <NUMBER> --json autoMergeRequest - Check CI status:
gh pr view <NUMBER> --json statusCheckRollup - Ensure branch protection requires status checks
- GitHub App tokens: Auto-expire after 1 hour (more secure than long-lived PATs)
- Minimal permissions: Only Contents (write) and Pull requests (read)
- Secrets encryption: All credentials stored as encrypted GitHub secrets
- Audit trail: All actions logged and attributed to the app/bot