Skip to content

Commit fe23714

Browse files
Copilotsnomiao
andauthored
Add GitHub Actions workflow to update PR branches with latest main (#226)
* Initial plan * feat: add workflow to update PR branches with latest main Co-authored-by: snomiao <[email protected]> * docs: add workflow documentation to README Co-authored-by: snomiao <[email protected]> * fix: remove trailing spaces from workflow file Co-authored-by: snomiao <[email protected]> * docs: add usage comments to workflow file Co-authored-by: snomiao <[email protected]> * fix: address code review feedback - Fix cleanup step to not hardcode branch name - Add PR number to merge commit message for traceability - Remove unused merge output file Co-authored-by: snomiao <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: snomiao <[email protected]>
1 parent eb7b2a8 commit fe23714

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Update PR Branches to Latest Main
2+
#
3+
# This workflow allows maintainers to update open PR branches with the latest changes from main.
4+
#
5+
# Usage:
6+
# - Via GitHub UI: Actions tab → Update PR Branches to Latest Main → Run workflow
7+
# - Via CLI: gh workflow run update-pr-branches.yml [-f pr_numbers="220,224"] [-f dry_run=true]
8+
#
9+
# See docs/update-pr-branches.md for detailed documentation.
10+
11+
name: Update PR Branches to Latest Main
12+
13+
on:
14+
workflow_dispatch:
15+
inputs:
16+
pr_numbers:
17+
description: 'Comma-separated PR numbers to update (leave empty to update all open PRs)'
18+
required: false
19+
type: string
20+
dry_run:
21+
description: 'Dry run mode (just check what would be updated)'
22+
required: false
23+
type: boolean
24+
default: false
25+
26+
permissions:
27+
contents: write
28+
pull-requests: write
29+
30+
jobs:
31+
update-pr-branches:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout repository
35+
uses: actions/checkout@v4
36+
with:
37+
fetch-depth: 0
38+
token: ${{ secrets.GITHUB_TOKEN }}
39+
40+
- name: Configure git
41+
run: |
42+
git config user.name "github-actions[bot]"
43+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
44+
45+
- name: Fetch latest main branch
46+
run: |
47+
git fetch origin main
48+
echo "Latest main commit: $(git rev-parse origin/main)"
49+
50+
- name: Update PR branches
51+
env:
52+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
PR_NUMBERS: ${{ inputs.pr_numbers }}
54+
DRY_RUN: ${{ inputs.dry_run }}
55+
run: |
56+
set -e
57+
58+
# Function to update a single PR
59+
update_pr() {
60+
local pr_number=$1
61+
local pr_branch=$(gh pr view "$pr_number" --json headRefName -q .headRefName)
62+
local pr_title=$(gh pr view "$pr_number" --json title -q .title)
63+
local pr_state=$(gh pr view "$pr_number" --json state -q .state)
64+
65+
echo "=================================================="
66+
echo "Processing PR #$pr_number: $pr_title"
67+
echo "Branch: $pr_branch"
68+
echo "State: $pr_state"
69+
70+
if [ "$pr_state" != "OPEN" ]; then
71+
echo "⏭️ Skipping - PR is not open"
72+
return
73+
fi
74+
75+
# Check if branch exists locally
76+
if ! git show-ref --verify --quiet "refs/remotes/origin/$pr_branch"; then
77+
echo "❌ Branch $pr_branch does not exist"
78+
return
79+
fi
80+
81+
# Checkout the PR branch
82+
git checkout "$pr_branch" 2>/dev/null || git checkout -b "$pr_branch" "origin/$pr_branch"
83+
84+
# Check if branch is already up to date
85+
git merge-base --is-ancestor origin/main HEAD && {
86+
echo "✅ Already up to date with main"
87+
return
88+
}
89+
90+
# Check for merge conflicts
91+
if ! git merge --no-commit --no-ff origin/main 2>&1; then
92+
echo "⚠️ Merge conflict detected. Aborting merge..."
93+
git merge --abort 2>/dev/null || true
94+
echo "❌ Cannot auto-update due to conflicts - manual intervention required"
95+
gh pr comment "$pr_number" --body "⚠️ Unable to automatically update this PR branch to latest main due to merge conflicts. Please resolve conflicts manually."
96+
return
97+
fi
98+
99+
# Abort the test merge
100+
git merge --abort 2>/dev/null || true
101+
102+
if [ "$DRY_RUN" = "true" ]; then
103+
echo "🔍 DRY RUN: Would merge main into $pr_branch"
104+
return
105+
fi
106+
107+
# Perform the actual merge
108+
if git merge origin/main -m "chore: merge latest main into PR #$pr_number branch"; then
109+
echo "✅ Successfully merged main into $pr_branch"
110+
111+
# Push the updated branch
112+
if git push origin "$pr_branch"; then
113+
echo "✅ Successfully pushed updated branch"
114+
gh pr comment "$pr_number" --body "🤖 This PR branch has been automatically updated with the latest changes from main."
115+
else
116+
echo "❌ Failed to push updated branch"
117+
fi
118+
else
119+
echo "❌ Failed to merge main into $pr_branch"
120+
git merge --abort 2>/dev/null || true
121+
fi
122+
}
123+
124+
# Get list of PRs to update
125+
if [ -n "$PR_NUMBERS" ]; then
126+
# Update specific PRs
127+
IFS=',' read -ra PR_ARRAY <<< "$PR_NUMBERS"
128+
for pr_num in "${PR_ARRAY[@]}"; do
129+
pr_num=$(echo "$pr_num" | tr -d ' ')
130+
update_pr "$pr_num"
131+
done
132+
else
133+
# Update all open PRs
134+
echo "Fetching all open PRs..."
135+
pr_list=$(gh pr list --state open --json number -q '.[].number')
136+
137+
if [ -z "$pr_list" ]; then
138+
echo "No open PRs found"
139+
exit 0
140+
fi
141+
142+
echo "Found $(echo "$pr_list" | wc -l) open PRs"
143+
144+
for pr_num in $pr_list; do
145+
update_pr "$pr_num"
146+
done
147+
fi
148+
149+
echo "=================================================="
150+
echo "✅ Update process completed"
151+
152+
- name: Cleanup
153+
if: always()
154+
run: |
155+
# Return to original branch or default branch
156+
git checkout "${GITHUB_REF#refs/heads/}" 2>/dev/null || git checkout main 2>/dev/null || git checkout "$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')" 2>/dev/null || true

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ pnpm run chromatic
6060

6161
See [docs/chromatic-setup.md](docs/chromatic-setup.md) for more details on our Chromatic CI/CD integration.
6262

63+
### Workflows & Automation
64+
65+
#### Update PR Branches
66+
67+
We provide a GitHub Actions workflow to automatically update open PR branches with the latest changes from `main`. This is useful for:
68+
- Keeping long-running PRs up-to-date
69+
- Reducing merge conflicts
70+
- Repository maintenance
71+
72+
See [docs/update-pr-branches.md](docs/update-pr-branches.md) for detailed usage instructions.
73+
6374
### Generate Code Stub based on OpenAPI Spec
6475

6576
Start the dev server.

docs/update-pr-branches.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Update PR Branches Workflow
2+
3+
## Overview
4+
5+
The "Update PR Branches to Latest Main" workflow allows maintainers to automatically update open pull request branches with the latest changes from the `main` branch. This helps keep PRs up-to-date and reduces merge conflicts.
6+
7+
## When to Use
8+
9+
- When `main` branch has significant updates and you want all open PRs to be current
10+
- Before merging long-running PRs that might have fallen behind
11+
- As part of regular repository maintenance
12+
13+
## How to Run
14+
15+
### Via GitHub UI
16+
17+
1. Go to the [Actions tab](../../actions/workflows/update-pr-branches.yml) in the repository
18+
2. Click on "Update PR Branches to Latest Main" workflow
19+
3. Click the "Run workflow" button
20+
4. Configure options:
21+
- **PR numbers**: Leave empty to update all open PRs, or specify comma-separated PR numbers (e.g., `220,224,226`)
22+
- **Dry run**: Check this to see what would be updated without making changes
23+
5. Click "Run workflow"
24+
25+
### Via GitHub CLI
26+
27+
Update all open PRs:
28+
```bash
29+
gh workflow run update-pr-branches.yml
30+
```
31+
32+
Update specific PRs:
33+
```bash
34+
gh workflow run update-pr-branches.yml -f pr_numbers="220,224,226"
35+
```
36+
37+
Dry run mode:
38+
```bash
39+
gh workflow run update-pr-branches.yml -f dry_run=true
40+
```
41+
42+
## What the Workflow Does
43+
44+
For each PR (or specified PRs):
45+
46+
1. **Checks PR status**: Skips closed PRs
47+
2. **Verifies branch exists**: Ensures the PR branch is accessible
48+
3. **Checks if up-to-date**: Skips if already current with main
49+
4. **Detects merge conflicts**: Tests merge and reports conflicts
50+
5. **Merges main branch**: Updates PR branch with latest main
51+
6. **Pushes changes**: Pushes updated branch back to GitHub
52+
7. **Comments on PR**: Adds comment confirming the update
53+
54+
## Handling Conflicts
55+
56+
If the workflow detects merge conflicts:
57+
58+
- The merge is aborted automatically
59+
- A comment is added to the PR noting manual intervention is needed
60+
- The PR author should:
61+
1. Pull latest changes from main
62+
2. Resolve conflicts locally
63+
3. Push the resolved changes
64+
65+
## Limitations
66+
67+
- **Protected branches**: Cannot push to branches with strict protection rules
68+
- **Forked repositories**: Cannot update PRs from forks (different repository)
69+
- **Draft PRs**: Updates all open PRs including drafts
70+
- **Permissions**: Requires write access to repository and PRs
71+
72+
## Best Practices
73+
74+
1. **Use dry run first**: Test with dry run mode before actual updates
75+
2. **Update specific PRs**: When possible, target specific PRs rather than all
76+
3. **Communicate**: Let PR authors know you're updating their branches
77+
4. **Monitor results**: Check workflow logs to see which PRs were updated
78+
5. **Handle conflicts promptly**: Address any reported conflicts quickly
79+
80+
## Troubleshooting
81+
82+
### Workflow fails with permission error
83+
- Check that `GITHUB_TOKEN` has write permissions
84+
- Verify branch protection rules allow GitHub Actions to push
85+
86+
### PR not updated
87+
- Check workflow logs for specific error messages
88+
- Verify PR is in OPEN state
89+
- Ensure branch exists and is accessible
90+
- Check for merge conflicts reported in comments
91+
92+
### Push fails
93+
- Branch may have protection rules preventing push
94+
- PR might be from a forked repository
95+
- Network issues or rate limiting
96+
97+
## Security Considerations
98+
99+
- Workflow uses built-in `GITHUB_TOKEN` - no additional secrets needed
100+
- Only repository collaborators can trigger the workflow
101+
- Branch protection rules are respected
102+
- All actions are logged and auditable
103+
104+
## Examples
105+
106+
### Example 1: Update all open PRs
107+
```bash
108+
gh workflow run update-pr-branches.yml
109+
```
110+
111+
### Example 2: Update specific PRs (220, 224, 226)
112+
```bash
113+
gh workflow run update-pr-branches.yml -f pr_numbers="220,224,226"
114+
```
115+
116+
### Example 3: Dry run to see what would be updated
117+
```bash
118+
gh workflow run update-pr-branches.yml -f dry_run=true
119+
```
120+
121+
## Related Workflows
122+
123+
- **api-gen.yml**: Automatically updates API type definitions
124+
- **react-ci.yml**: Runs CI checks on PRs
125+
- **unit-test.yml**: Runs unit tests on PRs
126+
127+
## Future Enhancements
128+
129+
Potential improvements for this workflow:
130+
131+
- [ ] Add option to auto-merge PRs that are ready and pass checks
132+
- [ ] Support for updating PRs based on labels
133+
- [ ] Slack/Discord notifications for update results
134+
- [ ] Automatic conflict resolution for simple conflicts
135+
- [ ] Schedule option to run weekly/monthly

0 commit comments

Comments
 (0)