A powerful GitHub Action for automatically managing staging branches with configurable sync, reset, and PR labeling functionality.
- ✅ Auto-sync staging branch - Automatically syncs commits from main branch to staging branch
- ✅ Scheduled reset - Resets staging branch to main on a configurable schedule (default: every Sunday at midnight)
- ✅ PR labeling - Automatically stage PRs when labeled with
to-stage, and mark them asstagedwhen complete - ✅ Smart label management - Removes
stagedlabels when PRs are removed from staging or when commits are no longer staged - ✅ Manual sync detection - Detects when commits are manually added to staging and syncs if needed
- ✅ Reports failures on the PR - Every failure to stage a PR is commented on that PR, with the git output and a link to the run
- ✅ Verified staging - Confirms the staging branch really contains the PR before marking it
staged, and retries a push lost to a concurrent update - ✅ Fully configurable - All branch names, labels, and behaviors are configurable
Create a workflow file (e.g., .github/workflows/auto-staging.yml) in your repository:
name: Auto Staging
on:
# Scheduled reset every Sunday at midnight
schedule:
- cron: '0 0 * * 0' # Sunday midnight UTC
# Trigger on push to main or staging
push:
branches:
- main
- staging
# Trigger on PR events (including label events)
pull_request:
branches:
- main
types: [opened, reopened, synchronize, labeled, unlabeled]
# Allow manual triggering
workflow_dispatch:
jobs:
auto-staging:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
main_branch: 'main'
staging_branch: 'staging'
to_stage_label: 'to-stage'
staged_label: 'staged'
github_token: ${{ secrets.GITHUB_TOKEN }}
enable_auto_sync: 'true'
enable_scheduled_reset: 'true'
enable_pr_labeling: 'true'All inputs are optional and have sensible defaults:
| Input | Description | Default | Required |
|---|---|---|---|
main_branch |
Name of the main branch | main |
No |
staging_branch |
Name of the staging branch | staging |
No |
to_stage_label |
Label to trigger staging a PR | to-stage |
No |
staged_label |
Label to mark a PR as staged | staged |
No |
github_token |
GitHub token for API calls | - | Yes |
reset_schedule |
Cron schedule for resetting staging branch | 0 0 * * 0 |
No |
enable_auto_sync |
Enable auto-sync of main branch commits to staging | true |
No |
enable_scheduled_reset |
Enable scheduled reset of staging branch | true |
No |
enable_pr_labeling |
Enable PR labeling functionality | true |
No |
When commits are pushed to the main branch, the action automatically:
- Checks if there are new commits in
mainthat aren't instaging - Merges those commits into
staging - Updates PR labels if any staged PRs are affected
On the configured schedule (default: Sunday midnight UTC), the action:
- Resets the
stagingbranch to matchmainexactly - Removes
stagedlabels from all PRs that were affected by the reset
When a PR is labeled with the to-stage label:
- The action fetches the current PR state from the API (to get up-to-date labels)
- Verifies the PR currently has the
to-stagelabel - Merges the PR into the
stagingbranch and pushes it - Re-fetches
stagingand verifies it actually contains the PR head commit - Adds the
stagedlabel to the PR and removes theto-stagelabel - If anything fails, the action posts a comment on the PR explaining what went wrong and does not add the
stagedlabel
If the push is rejected because something else updated staging at the same
time, the action re-syncs and retries (3 attempts) before giving up and
commenting. The staged label is only applied once the PR head commit is
confirmed to be an ancestor of the remote staging branch, so a green run
always means the change really is staged.
Note: The action fetches current PR labels from the API (not from the event payload) to handle labels added after the PR was opened, since github.event.pull_request.labels only contains labels from when the PR was first created.
When a PR is reopened or synchronized:
- The action fetches the current PR state from the API
- Checks if the PR currently has the
stagedlabel - Verifies if all PR commits are still in the
stagingbranch - Removes the
stagedlabel if commits are no longer staged
When commits are manually pushed to the staging branch:
- The action detects the push event
- Checks if there are commits in
mainthat aren't instaging - Automatically syncs those commits if found
- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
main_branch: 'master'
staging_branch: 'develop'
github_token: ${{ secrets.GITHUB_TOKEN }}- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
to_stage_label: 'ready-for-staging'
staged_label: 'in-staging'
github_token: ${{ secrets.GITHUB_TOKEN }}- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
enable_scheduled_reset: 'false'
github_token: ${{ secrets.GITHUB_TOKEN }}- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
reset_schedule: '0 2 * * 1' # Every Monday at 2 AM UTC
github_token: ${{ secrets.GITHUB_TOKEN }}- name: Run Auto Staging
uses: Plexverse/auto-staging@v1
with:
enable_pr_labeling: 'false'
github_token: ${{ secrets.GITHUB_TOKEN }}The action requires the following permissions:
contents: write- To push changes to the staging branchpull-requests: write- To add/remove labels on PRsissues: write- To post comments on PRs (PRs are treated as issues in the GitHub API)
jq- The action will automatically installjqif it's not available- Git with proper authentication configured
- A GitHub token with appropriate permissions
The action will automatically create the staging branch from the main branch if it doesn't exist.
If a merge conflict occurs when syncing or staging a PR, the action will:
- Log a warning
- Abort the merge
- Not push any changes
- Post a comment on affected PRs explaining the conflict and how to resolve it
- For PR staging, not add the
stagedlabel - Fail the job, so the run shows up red rather than silently passing
This was possible in versions before the failure-reporting fix: a conflicted
merge was mistakenly read as a success, so the run went green, the PR was
labelled staged, and no comment was posted even though staging had not
moved. Re-run against the current v2 tag, which fails the job and comments on
the PR instead. If a comment still does not appear, check the run log for
GitHub rejected the comment - that means the token is missing
issues: write / pull-requests: write.
Ensure that:
- The
enable_pr_labelinginput is set totrue - The GitHub token has
pull-requests: writepermission - The labels (
to-stageandstaged) exist in your repository
This action is provided as-is. Modify as needed for your use case.
