Skip to content

Commit 478fc60

Browse files
riderxclaude
andcommitted
feat: add workflow to sync upstream main to main and plus branches
Adds a new workflow that runs daily at 5 AM UTC to sync the upstream main branch directly to both the main and plus branches. The workflow: - Tries fast-forward merge first for clean history - Falls back to regular merge if fast-forward not possible - Only creates a PR if merge conflicts cannot be auto-resolved - Can be manually triggered to sync specific branches This ensures both branches stay current with upstream without requiring manual intervention for non-conflicting changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2b487bf commit 478fc60

1 file changed

Lines changed: 265 additions & 0 deletions

File tree

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
name: Sync Branches from Upstream
2+
3+
on:
4+
schedule:
5+
# Run daily at 5 AM UTC (before the PR sync at 6 AM)
6+
- cron: '0 5 * * *'
7+
workflow_dispatch:
8+
inputs:
9+
branch:
10+
description: 'Which branch to sync'
11+
required: true
12+
default: 'both'
13+
type: choice
14+
options:
15+
- both
16+
- main
17+
- plus
18+
19+
env:
20+
UPSTREAM_REPO: ionic-team/capacitor
21+
22+
jobs:
23+
sync-main-branch:
24+
if: github.event_name == 'schedule' || github.event.inputs.branch == 'both' || github.event.inputs.branch == 'main'
25+
runs-on: ubuntu-latest
26+
timeout-minutes: 30
27+
permissions:
28+
contents: write
29+
pull-requests: write
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v5
33+
with:
34+
fetch-depth: 0
35+
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
36+
ref: main
37+
38+
- name: Configure Git
39+
run: |
40+
git config user.name "Capacitor+ Bot"
41+
git config user.email "bot@capgo.app"
42+
43+
- name: Add upstream and fetch
44+
run: |
45+
git remote add upstream https://github.com/${{ env.UPSTREAM_REPO }}.git || true
46+
git fetch upstream main
47+
48+
- name: Check for new commits
49+
id: check-commits
50+
run: |
51+
UPSTREAM_COMMITS=$(git rev-list main..upstream/main --count)
52+
echo "upstream_commits=$UPSTREAM_COMMITS" >> $GITHUB_OUTPUT
53+
54+
if [ "$UPSTREAM_COMMITS" -gt 0 ]; then
55+
echo "Found $UPSTREAM_COMMITS new commits from upstream"
56+
echo "has_updates=true" >> $GITHUB_OUTPUT
57+
else
58+
echo "No new commits from upstream"
59+
echo "has_updates=false" >> $GITHUB_OUTPUT
60+
fi
61+
62+
- name: Try fast-forward merge
63+
if: steps.check-commits.outputs.has_updates == 'true'
64+
id: fast-forward
65+
run: |
66+
# Try to fast-forward merge
67+
if git merge upstream/main --ff-only; then
68+
echo "Fast-forward merge successful!"
69+
echo "can_fast_forward=true" >> $GITHUB_OUTPUT
70+
else
71+
echo "Fast-forward not possible, will try regular merge"
72+
echo "can_fast_forward=false" >> $GITHUB_OUTPUT
73+
fi
74+
75+
- name: Push if fast-forward succeeded
76+
if: steps.fast-forward.outputs.can_fast_forward == 'true'
77+
run: |
78+
git push origin main
79+
echo "Successfully pushed upstream changes to main branch"
80+
81+
- name: Try regular merge
82+
if: steps.check-commits.outputs.has_updates == 'true' && steps.fast-forward.outputs.can_fast_forward == 'false'
83+
id: regular-merge
84+
run: |
85+
# Reset to original state
86+
git reset --hard origin/main
87+
88+
# Try regular merge
89+
if git merge upstream/main --no-edit -m "chore: sync main with upstream"; then
90+
echo "Regular merge successful!"
91+
git push origin main
92+
echo "merge_success=true" >> $GITHUB_OUTPUT
93+
else
94+
echo "Merge conflict detected"
95+
git merge --abort
96+
echo "merge_success=false" >> $GITHUB_OUTPUT
97+
fi
98+
99+
- name: Create PR for conflicts
100+
if: steps.regular-merge.outputs.merge_success == 'false'
101+
env:
102+
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
103+
run: |
104+
# Create a branch for the PR
105+
SYNC_BRANCH="sync/main-upstream-$(date +%Y%m%d-%H%M%S)"
106+
git checkout -b "$SYNC_BRANCH" origin/main
107+
108+
# Try merge again to show conflicts
109+
git merge upstream/main --no-commit || true
110+
111+
# Check for existing PR
112+
EXISTING_PR=$(gh pr list --base main --search "sync main with upstream" --state open --json number -q '.[0].number')
113+
if [ -n "$EXISTING_PR" ]; then
114+
echo "PR already exists: #$EXISTING_PR"
115+
exit 0
116+
fi
117+
118+
# Abort and create a clean branch
119+
git merge --abort
120+
git checkout -b "$SYNC_BRANCH" origin/main
121+
122+
# Cherry-pick or merge with conflicts marked
123+
git merge upstream/main -X theirs --no-edit -m "chore: sync main with upstream (conflicts resolved with upstream)" || {
124+
# If even that fails, just take upstream
125+
git reset --hard upstream/main
126+
}
127+
128+
git push origin "$SYNC_BRANCH"
129+
130+
gh pr create \
131+
--base main \
132+
--head "$SYNC_BRANCH" \
133+
--title "chore: sync main with upstream (conflicts)" \
134+
--body "## Merge Conflict Resolution Required
135+
136+
The automatic sync of main branch with upstream encountered merge conflicts.
137+
138+
**Action needed:** Review the changes and resolve any remaining conflicts.
139+
140+
---
141+
*This PR was created automatically by the Capacitor+ sync workflow*" \
142+
--label "upstream-sync,needs-attention,merge-conflict"
143+
144+
sync-plus-branch:
145+
if: github.event_name == 'schedule' || github.event.inputs.branch == 'both' || github.event.inputs.branch == 'plus'
146+
runs-on: ubuntu-latest
147+
timeout-minutes: 30
148+
permissions:
149+
contents: write
150+
pull-requests: write
151+
steps:
152+
- name: Checkout
153+
uses: actions/checkout@v5
154+
with:
155+
fetch-depth: 0
156+
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
157+
ref: plus
158+
159+
- name: Configure Git
160+
run: |
161+
git config user.name "Capacitor+ Bot"
162+
git config user.email "bot@capgo.app"
163+
164+
- name: Add upstream and fetch
165+
run: |
166+
git remote add upstream https://github.com/${{ env.UPSTREAM_REPO }}.git || true
167+
git fetch upstream main
168+
169+
- name: Check for new commits
170+
id: check-commits
171+
run: |
172+
# Get the merge base between plus and upstream/main
173+
MERGE_BASE=$(git merge-base plus upstream/main)
174+
175+
# Count commits on upstream since merge base
176+
UPSTREAM_COMMITS=$(git rev-list $MERGE_BASE..upstream/main --count)
177+
echo "upstream_commits=$UPSTREAM_COMMITS" >> $GITHUB_OUTPUT
178+
179+
if [ "$UPSTREAM_COMMITS" -gt 0 ]; then
180+
echo "Found $UPSTREAM_COMMITS new commits from upstream"
181+
echo "has_updates=true" >> $GITHUB_OUTPUT
182+
else
183+
echo "No new commits from upstream"
184+
echo "has_updates=false" >> $GITHUB_OUTPUT
185+
fi
186+
187+
- name: Try merge
188+
if: steps.check-commits.outputs.has_updates == 'true'
189+
id: merge
190+
run: |
191+
# Try to merge upstream/main into plus
192+
if git merge upstream/main --no-edit -m "chore: sync plus with upstream main"; then
193+
echo "Merge successful!"
194+
echo "merge_success=true" >> $GITHUB_OUTPUT
195+
else
196+
echo "Merge conflict detected"
197+
git merge --abort
198+
echo "merge_success=false" >> $GITHUB_OUTPUT
199+
fi
200+
201+
- name: Push if merge succeeded
202+
if: steps.merge.outputs.merge_success == 'true'
203+
run: |
204+
git push origin plus
205+
echo "Successfully pushed upstream changes to plus branch"
206+
207+
- name: Create PR for conflicts
208+
if: steps.merge.outputs.merge_success == 'false'
209+
env:
210+
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
211+
run: |
212+
# Check for existing PR
213+
EXISTING_PR=$(gh pr list --base plus --search "sync plus with upstream" --state open --json number -q '.[0].number')
214+
if [ -n "$EXISTING_PR" ]; then
215+
echo "PR already exists: #$EXISTING_PR"
216+
exit 0
217+
fi
218+
219+
# Create a branch for the PR
220+
SYNC_BRANCH="sync/plus-upstream-$(date +%Y%m%d-%H%M%S)"
221+
git checkout -b "$SYNC_BRANCH" plus
222+
223+
# Merge with conflicts resolved using upstream version
224+
git merge upstream/main -X theirs --no-edit -m "chore: sync plus with upstream main (auto-resolved)" || {
225+
# If even that fails, create a proper merge commit
226+
git reset --hard plus
227+
git merge upstream/main --no-commit || true
228+
229+
# Add all files (with conflict markers if any)
230+
git add -A
231+
232+
# Create commit
233+
git commit -m "chore: sync plus with upstream main (needs conflict resolution)" || {
234+
echo "No changes to commit"
235+
exit 0
236+
}
237+
}
238+
239+
git push origin "$SYNC_BRANCH"
240+
241+
gh pr create \
242+
--base plus \
243+
--head "$SYNC_BRANCH" \
244+
--title "chore: sync plus with upstream main (conflicts)" \
245+
--body "## Merge Conflict Resolution Required
246+
247+
The automatic sync of plus branch with upstream main encountered merge conflicts.
248+
249+
**Action needed:** Review the changes and resolve any remaining conflicts.
250+
251+
### Note
252+
This PR attempts to auto-resolve conflicts by preferring upstream changes.
253+
Please verify that our Capacitor+ customizations (package names, workflows, etc.) are preserved.
254+
255+
---
256+
*This PR was created automatically by the Capacitor+ sync workflow*" \
257+
--label "upstream-sync,needs-attention,merge-conflict"
258+
259+
- name: Trigger Claude review for auto-merged changes
260+
if: steps.merge.outputs.merge_success == 'true'
261+
env:
262+
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
263+
run: |
264+
echo "Changes pushed directly to plus branch"
265+
echo "Consider running publish workflow if needed"

0 commit comments

Comments
 (0)