fix: handle existing sync PRs in upstream-sync workflow

Add idempotency to the merge-upstream job:

1. Check for existing open PR from chore/upstream-sync-* branch
2. If PR exists and branch is up to date with upstream → skip
3. If PR exists but upstream has new commits → checkout existing
   branch, merge new upstream commits, run Claude to review/test
4. If no PR exists → create new branch (existing behavior)

Also update Claude's prompt to handle both create and update PR
cases, so it knows to push to the existing PR branch instead of
creating a duplicate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-27 13:15:50 +02:00
1 parent 88e7747d2f
commit b1524d7857
2 files changed
+108 -14

No files matched your search

+52 -6
View File
@@ -187,6 +187,27 @@ jobs:
with:
fetch-depth: '0'
token: ${{ secrets.CLAUDE_BOT_TOKEN }}
- name: Check for existing sync PR
id: existing-pr
run: |-
# Search for open PRs from chore/upstream-sync-* branches
RESPONSE=$(curl -s \
-H "Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}/pulls?state=open&limit=50")
PR_BRANCH=$(echo "$RESPONSE" | jq -r '[.[] | select(.head.ref | startswith("chore/upstream-sync-"))] | .[0].head.ref // ""')
if [ -n "$PR_BRANCH" ]; then
PR_NUMBER=$(echo "$RESPONSE" | jq -r '[.[] | select(.head.ref | startswith("chore/upstream-sync-"))] | .[0].number')
echo "Found existing sync PR #$PR_NUMBER on branch $PR_BRANCH"
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
echo "pr_branch=$PR_BRANCH" >> "$GITHUB_OUTPUT"
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
else
echo "No existing sync PR found"
echo "pr_exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Prepare upstream merge
id: prepare
run: |-
@@ -196,6 +217,29 @@ jobs:
git remote add upstream https://github.com/anthropics/claude-code-action.git
git fetch upstream main --quiet
# Check if existing PR branch is already up to date with upstream
if [ "${{ steps.existing-pr.outputs.pr_exists }}" = "true" ]; then
PR_BRANCH="${{ steps.existing-pr.outputs.pr_branch }}"
git checkout "$PR_BRANCH"
# Check if upstream/main is already merged into the PR branch
BEHIND=$(git rev-list "$PR_BRANCH"..upstream/main --count)
if [ "$BEHIND" -eq 0 ]; then
echo "Existing PR branch $PR_BRANCH is already up to date with upstream"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Existing PR branch $PR_BRANCH is $BEHIND commits behind upstream — updating"
BRANCH="$PR_BRANCH"
else
BRANCH="chore/upstream-sync-$(date +%Y%m%d)"
git checkout -b "$BRANCH"
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
# Files our fork has modified vs upstream
FORK_FILES=$(git log upstream/main..main --name-only --pretty=format: | sort -u | grep -v '^$')
echo "=== Fork-modified files ==="
@@ -226,10 +270,6 @@ jobs:
echo "${AFFECTED:-(none)}" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
BRANCH="chore/upstream-sync-$(date +%Y%m%d)"
git checkout -b "$BRANCH"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
if git merge upstream/main --no-edit; then
echo "merge_clean=true" >> "$GITHUB_OUTPUT"
echo "Merge completed cleanly"
@@ -252,9 +292,12 @@ jobs:
curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
fi
if: steps.prepare.outputs.skip != 'true'
- name: Install dependencies
run: bun install
if: steps.prepare.outputs.skip != 'true'
- name: 'Claude: review merge and create PR'
if: steps.prepare.outputs.skip != 'true'
uses: https://${{ secrets.READ_REPO_TOKEN }}@git.wxs.ro/actions/claude-code-action@main
with:
prompt: |-
@@ -264,6 +307,7 @@ jobs:
- Branch: ${{ steps.prepare.outputs.branch }}
- ${{ needs.check-upstream.outputs.behind }} commits behind upstream, ${{ needs.check-upstream.outputs.ahead }} commits ahead (our Forgejo changes)
- Tracking issue: #${{ needs.check-upstream.outputs.issue_number }}
- Existing PR: ${{ steps.existing-pr.outputs.pr_exists }} (if true, PR #${{ steps.existing-pr.outputs.pr_number }} already exists — update it instead of creating a new one)
- Merge was attempted. Status: ${{ steps.prepare.outputs.merge_clean }} (true = clean, false = has conflicts)
## Impact analysis
@@ -294,8 +338,10 @@ jobs:
2. Run typecheck: bun run typecheck
3. Check formatting: bun run format:check — if issues, fix with bun run format
4. Commit any remaining changes and push the branch
5. Create a PR targeting main via the Forgejo API:
POST $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls
5. Push the branch, then create or update the PR:
- If existing PR is "true": PR #${{ steps.existing-pr.outputs.pr_number }} already exists. Just push the branch — the PR updates automatically.
Optionally update the PR body via PATCH $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/${{ steps.existing-pr.outputs.pr_number }}
- If existing PR is "false": Create a new PR via POST $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls
Auth: token $GITHUB_TOKEN
Title: "chore: merge ${{ needs.check-upstream.outputs.behind }} upstream commits from anthropics/claude-code-action"
Body should include: