Skip to content

Sync Upstream PRs

Sync Upstream PRs #53

Workflow file for this run

name: Sync Upstream PRs
on:
schedule:
# Run daily at 6 AM UTC
- cron: '0 6 * * *'
workflow_dispatch:
inputs:
sync_mode:
description: 'What to sync'
required: true
default: 'all_open_prs'
type: choice
options:
- all_open_prs
- main_branch
- specific_pr
pr_number:
description: 'Specific PR number (only for specific_pr mode)'
required: false
type: string
force_fmt:
description: 'Force run formatter on all existing sync branches'
required: false
default: false
type: boolean
env:
UPSTREAM_REPO: ionic-team/capacitor
UPSTREAM_BRANCH: main
jobs:
# Job 1: Fetch all open PRs from upstream and create corresponding PRs
sync-open-prs:
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.sync_mode == 'all_open_prs')
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
filter: blob:none
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm install
- name: Configure Git
run: |
git config user.name "Capacitor+ Bot"
git config user.email "bot@capgo.app"
- name: Fetch and sync all open PRs from upstream
env:
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
run: |
echo "Fetching open PRs from upstream repository..."
# Get all open PRs from upstream (only from forks/external contributors)
OPEN_PRS=$(gh pr list \
--repo ${{ env.UPSTREAM_REPO }} \
--state open \
--json number,title,headRefName,headRepositoryOwner,author,url,isDraft \
--limit 100)
echo "Found PRs:"
echo "$OPEN_PRS" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login)"'
# Filter to non-draft PRs and exclude our own PRs (riderx is the maintainer)
# This prevents syncing PRs we created ourselves on the upstream repo
ALL_PRS=$(echo "$OPEN_PRS" | jq '[.[] | select(.isDraft == false and .author.login != "riderx")]')
echo ""
echo "All open PRs (non-draft):"
echo "$ALL_PRS" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login)"'
PR_COUNT=$(echo "$ALL_PRS" | jq 'length')
echo ""
echo "Total PRs to sync: $PR_COUNT"
if [ "$PR_COUNT" -eq 0 ]; then
echo "No PRs to sync"
exit 0
fi
# Process each PR
echo "$ALL_PRS" | jq -c '.[]' | while read -r pr; do
PR_NUMBER=$(echo "$pr" | jq -r '.number')
PR_TITLE=$(echo "$pr" | jq -r '.title')
PR_AUTHOR=$(echo "$pr" | jq -r '.author.login')
PR_URL=$(echo "$pr" | jq -r '.url')
echo ""
echo "=========================================="
echo "Processing PR #$PR_NUMBER: $PR_TITLE"
echo "Author: @$PR_AUTHOR"
echo "=========================================="
# Fetch the PR branch from upstream first to check for updates
echo "Fetching PR #$PR_NUMBER from upstream..."
git fetch https://github.com/${{ env.UPSTREAM_REPO }}.git pull/$PR_NUMBER/head:upstream-pr-$PR_NUMBER || {
echo "Failed to fetch PR #$PR_NUMBER, skipping..."
continue
}
SYNC_BRANCH="sync/upstream-pr-$PR_NUMBER"
UPSTREAM_HEAD=$(git rev-parse upstream-pr-$PR_NUMBER)
# Check if we already have a PR for this upstream PR
EXISTING_PR=$(gh pr list \
--state open \
--search "upstream PR #$PR_NUMBER" \
--json number,headRefName \
--limit 1)
if [ "$(echo "$EXISTING_PR" | jq 'length')" -gt 0 ]; then
EXISTING_PR_NUM=$(echo "$EXISTING_PR" | jq -r '.[0].number')
echo "PR #$EXISTING_PR_NUM for upstream #$PR_NUMBER already exists"
# Check if upstream has new commits by comparing with our sync branch
git fetch origin "$SYNC_BRANCH" 2>/dev/null || true
if git rev-parse "origin/$SYNC_BRANCH" >/dev/null 2>&1; then
# Get the upstream commit that was merged into our branch
# We need to check if the upstream PR has new commits
OUR_BRANCH_HEAD=$(git rev-parse "origin/$SYNC_BRANCH")
# Check if force_fmt is enabled
FORCE_FMT="${{ github.event.inputs.force_fmt }}"
# Check if upstream commit is already in our branch
if git merge-base --is-ancestor "$UPSTREAM_HEAD" "origin/$SYNC_BRANCH" 2>/dev/null; then
if [ "$FORCE_FMT" = "true" ]; then
echo "Force fmt enabled - will re-format even though branch is up to date..."
else
echo "Our sync branch is up to date with upstream PR, skipping..."
git branch -D upstream-pr-$PR_NUMBER 2>/dev/null || true
continue
fi
else
echo "Upstream PR has new commits! Updating our sync branch..."
# Continue to re-sync the branch
fi
else
echo "Sync branch doesn't exist on remote, will recreate..."
fi
else
# Also check closed PRs to avoid re-creating rejected ones
CLOSED_PR=$(gh pr list \
--state closed \
--search "upstream PR #$PR_NUMBER" \
--json number \
--limit 1)
if [ "$(echo "$CLOSED_PR" | jq 'length')" -gt 0 ]; then
echo "PR for upstream #$PR_NUMBER was previously closed, skipping..."
git branch -D upstream-pr-$PR_NUMBER 2>/dev/null || true
continue
fi
fi
# Delete the local branch if it exists
git branch -D "$SYNC_BRANCH" 2>/dev/null || true
# Create sync branch from plus
git checkout plus
git checkout -b "$SYNC_BRANCH"
# Try to merge the PR
echo "Merging PR #$PR_NUMBER..."
if git merge upstream-pr-$PR_NUMBER --no-edit -m "chore: sync upstream PR #$PR_NUMBER from @$PR_AUTHOR"; then
echo "Merge successful!"
# Run formatter to ensure lint passes
echo "Running formatter..."
npm run fmt || echo "Formatter had some issues, continuing..."
# Check if fmt made any changes and commit them
if ! git diff --quiet; then
echo "Formatter made changes, committing..."
git add -A
git commit -m "style: auto-format code after upstream sync"
fi
# Push the branch
git push origin "$SYNC_BRANCH" --force
# Create PR body
PR_BODY=$(cat <<EOF
## Upstream PR Sync
This PR syncs changes from an external contributor's PR on the official Capacitor repository.
### Original PR
- **PR:** [#$PR_NUMBER]($PR_URL)
- **Title:** $PR_TITLE
- **Author:** @$PR_AUTHOR
### Automation
- CI will run automatically
- Claude Code will review for security/breaking changes
- If approved, this PR will be auto-merged
- A new release will be published automatically
---
*Synced from upstream by Capacitor+ Bot*
EOF
)
# Create the PR
gh pr create \
--base plus \
--head "$SYNC_BRANCH" \
--title "chore: sync upstream PR #$PR_NUMBER - $PR_TITLE" \
--body "$PR_BODY" \
--label "upstream-sync,automated" || {
echo "Failed to create PR, it may already exist"
}
echo "Created PR for upstream #$PR_NUMBER"
else
echo "Merge conflict detected for PR #$PR_NUMBER"
git merge --abort
# Create an issue for manual intervention
gh issue create \
--title "Merge conflict: Upstream PR #$PR_NUMBER - $PR_TITLE" \
--body "The sync of upstream PR #$PR_NUMBER from @$PR_AUTHOR encountered merge conflicts.
**Original PR:** $PR_URL
Please resolve the conflicts manually." \
--label "upstream-sync,needs-attention,merge-conflict" || {
echo "Issue may already exist"
}
fi
# Clean up
git checkout plus
git branch -D upstream-pr-$PR_NUMBER 2>/dev/null || true
done
echo ""
echo "Sync complete!"
# Job 2: Sync main branch from upstream
sync-main-branch:
if: github.event_name == 'workflow_dispatch' && github.event.inputs.sync_mode == 'main_branch'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
filter: blob:none
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm install
- name: Configure Git
run: |
git config user.name "Capacitor+ Bot"
git config user.email "bot@capgo.app"
- name: Sync main branch from upstream
env:
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
run: |
git remote add upstream https://github.com/${{ env.UPSTREAM_REPO }}.git || true
git fetch upstream
git checkout plus
git fetch upstream main
# Check if there are new commits from upstream
UPSTREAM_COMMITS=$(git rev-list plus..upstream/main --count)
if [ "$UPSTREAM_COMMITS" -eq 0 ]; then
echo "No new commits from upstream main branch"
exit 0
fi
echo "Found $UPSTREAM_COMMITS new commits from upstream"
# Generate branch name with date
SYNC_BRANCH="sync/upstream-main-$(date +%Y%m%d-%H%M%S)"
# Create sync branch from plus
git checkout -b "$SYNC_BRANCH" plus
# Merge upstream changes
if git merge upstream/main --no-edit -m "chore: sync with upstream main branch"; then
# Run formatter to ensure lint passes
echo "Running formatter..."
npm run fmt || echo "Formatter had some issues, continuing..."
# Check if fmt made any changes and commit them
if ! git diff --quiet; then
echo "Formatter made changes, committing..."
git add -A
git commit -m "style: auto-format code after upstream sync"
fi
git push origin "$SYNC_BRANCH"
PR_BODY=$(cat <<'EOF'
## Upstream Main Branch Sync
This PR syncs the latest changes from the official Capacitor main branch.
### Automation
- CI will run automatically
- Claude Code will review for security/breaking changes
- If approved, this PR will be auto-merged
---
*Synced from upstream by Capacitor+ Bot*
EOF
)
gh pr create \
--base plus \
--head "$SYNC_BRANCH" \
--title "chore: sync with upstream main $(date +%Y-%m-%d)" \
--body "$PR_BODY" \
--label "upstream-sync,automated"
else
echo "Merge conflicts detected"
git merge --abort
gh issue create \
--title "Merge conflict: Upstream main sync $(date +%Y-%m-%d)" \
--body "The automated upstream main branch sync encountered merge conflicts. Please resolve them manually." \
--label "upstream-sync,needs-attention,merge-conflict"
fi
# Job 3: Sync a specific PR by number
sync-specific-pr:
if: github.event_name == 'workflow_dispatch' && github.event.inputs.sync_mode == 'specific_pr' && github.event.inputs.pr_number != ''
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
filter: blob:none
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm install
- name: Configure Git
run: |
git config user.name "Capacitor+ Bot"
git config user.email "bot@capgo.app"
- name: Sync specific PR
env:
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
PR_NUMBER: ${{ github.event.inputs.pr_number }}
run: |
echo "Syncing specific PR #$PR_NUMBER from upstream..."
# Get PR info
PR_INFO=$(gh pr view $PR_NUMBER --repo ${{ env.UPSTREAM_REPO }} --json title,author,url,state)
PR_TITLE=$(echo "$PR_INFO" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_INFO" | jq -r '.author.login')
PR_URL=$(echo "$PR_INFO" | jq -r '.url')
PR_STATE=$(echo "$PR_INFO" | jq -r '.state')
echo "PR #$PR_NUMBER: $PR_TITLE"
echo "Author: @$PR_AUTHOR"
echo "State: $PR_STATE"
if [ "$PR_STATE" != "OPEN" ]; then
echo "Warning: PR #$PR_NUMBER is not open (state: $PR_STATE)"
fi
# Fetch the PR
git fetch https://github.com/${{ env.UPSTREAM_REPO }}.git pull/$PR_NUMBER/head:upstream-pr-$PR_NUMBER
# Create branch for this PR
SYNC_BRANCH="sync/upstream-pr-$PR_NUMBER"
# Delete existing branch if any
git branch -D "$SYNC_BRANCH" 2>/dev/null || true
git push origin --delete "$SYNC_BRANCH" 2>/dev/null || true
git checkout plus
git checkout -b "$SYNC_BRANCH"
# Merge the PR commits
if git merge upstream-pr-$PR_NUMBER --no-edit -m "chore: sync upstream PR #$PR_NUMBER from @$PR_AUTHOR"; then
# Run formatter to ensure lint passes
echo "Running formatter..."
npm run fmt || echo "Formatter had some issues, continuing..."
# Check if fmt made any changes and commit them
if ! git diff --quiet; then
echo "Formatter made changes, committing..."
git add -A
git commit -m "style: auto-format code after upstream sync"
fi
git push origin "$SYNC_BRANCH" --force
PR_BODY=$(cat <<EOF
## Upstream PR Sync
This PR syncs changes from an external contributor's PR on the official Capacitor repository.
### Original PR
- **PR:** [#$PR_NUMBER]($PR_URL)
- **Title:** $PR_TITLE
- **Author:** @$PR_AUTHOR
### Automation
- CI will run automatically
- Claude Code will review for security/breaking changes
- If approved, this PR will be auto-merged
---
*Synced from upstream by Capacitor+ Bot*
EOF
)
gh pr create \
--base plus \
--head "$SYNC_BRANCH" \
--title "chore: sync upstream PR #$PR_NUMBER - $PR_TITLE" \
--body "$PR_BODY" \
--label "upstream-sync,automated"
echo "Successfully created PR for upstream #$PR_NUMBER"
else
echo "Merge conflicts detected for PR #$PR_NUMBER"
git merge --abort
gh issue create \
--title "Merge conflict: Upstream PR #$PR_NUMBER - $PR_TITLE" \
--body "The sync of upstream PR #$PR_NUMBER from @$PR_AUTHOR encountered merge conflicts.
**Original PR:** $PR_URL
Please resolve the conflicts manually." \
--label "upstream-sync,needs-attention,merge-conflict"
fi