From 8e4896fa3a47da2dbf7a52862eb3aaba6e906083 Mon Sep 17 00:00:00 2001 From: "alex-nsheaps[bot]" <279051173+alex-nsheaps[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 10:30:13 -0400 Subject: [PATCH 1/3] feat(sync-main-to-edge): open a PR when direct push can't fast-forward MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reusable sync workflow relies on aormsby/Fork-Sync-With-Upstream-action to fast-forward upstream -> target. That push fails when the target (edge) branch has diverged from upstream or is protected. Previously the job just failed. Now the sync step is continue-on-error and, on failure, we open a PR upstream -> target instead (idempotent — skips if one is already open, and tolerates the no-diff case). Adds pull-requests: write for PR creation. Co-Authored-By: Agent Alex Picard --- .github/workflows/sync-main-to-edge.yaml | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync-main-to-edge.yaml b/.github/workflows/sync-main-to-edge.yaml index 650915f..0e9df25 100644 --- a/.github/workflows/sync-main-to-edge.yaml +++ b/.github/workflows/sync-main-to-edge.yaml @@ -32,6 +32,7 @@ on: permissions: contents: write + pull-requests: write jobs: plan: @@ -127,9 +128,14 @@ jobs: persist-credentials: false fetch-depth: 0 + # The aormsby action fast-forwards upstream -> target. It fails when the + # branches have diverged or when branch protection rejects the push. + # continue-on-error lets us fall back to opening a PR in that case instead + # of failing the whole job. - name: Sync upstream changes if: steps.check.outputs.exists == 'true' id: sync + continue-on-error: true uses: aormsby/Fork-Sync-With-Upstream-action@83d78e8c4bf524e79b091cf257deba4dee9e60df # v3.4.3 with: target_sync_branch: ${{ matrix.target }} @@ -138,10 +144,35 @@ jobs: upstream_sync_repo: ${{ github.repository }} test_mode: ${{ inputs.test_mode }} + # When the direct push could not happen (diverged history or branch + # protection), open a PR upstream -> target instead. Idempotent: skips if + # a PR is already open for the same head/base, and tolerates "no diff". + - name: Open PR if direct sync could not push + if: steps.check.outputs.exists == 'true' && steps.sync.outcome == 'failure' + env: + UPSTREAM: ${{ matrix.upstream }} + TARGET: ${{ matrix.target }} + REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + existing=$(gh pr list --repo "$REPO" --base "$TARGET" --head "$UPSTREAM" \ + --state open --json number -q '.[0].number' || true) + if [ -n "$existing" ]; then + echo "PR #$existing already open for $UPSTREAM -> $TARGET; nothing to do." + exit 0 + fi + gh pr create --repo "$REPO" --base "$TARGET" --head "$UPSTREAM" \ + --title "sync: $UPSTREAM -> $TARGET" \ + --body "Automated sync could not fast-forward \`$TARGET\` from \`$UPSTREAM\` (diverged history or branch protection). Merging via PR instead." \ + || echo "No PR created (possibly no diff between $UPSTREAM and $TARGET)." + - name: Report if: steps.check.outputs.exists == 'true' run: | - if [ "${{ steps.sync.outputs.has_new_commits }}" = "true" ]; then + if [ "${{ steps.sync.outcome }}" = "failure" ]; then + echo "Direct sync could not push ${{ matrix.upstream }} -> ${{ matrix.target }}; opened/ensured a PR instead." + elif [ "${{ steps.sync.outputs.has_new_commits }}" = "true" ]; then echo "Synced new commits from ${{ matrix.upstream }} into ${{ matrix.target }}." else echo "${{ matrix.target }} already up-to-date with ${{ matrix.upstream }}." From e74d037127314110d95f8a7a281695507294c055 Mon Sep 17 00:00:00 2001 From: "alex-nsheaps[bot]" <279051173+alex-nsheaps[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 11:16:10 -0400 Subject: [PATCH 2/3] fix(sync): add workflow run self-link to PR body (review feedback) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR body now includes `Workflow run: //actions/runs/` matching the nsheaps convention used in check.yaml. Partial fix for Nate's review on PR #48 — comment 2 (line 167). Comment 1 (line 165, reusable action) blocked pending Nate's clarification. Co-Authored-By: Agent Alex Picard --- .github/workflows/sync-main-to-edge.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync-main-to-edge.yaml b/.github/workflows/sync-main-to-edge.yaml index 0e9df25..7d6d35c 100644 --- a/.github/workflows/sync-main-to-edge.yaml +++ b/.github/workflows/sync-main-to-edge.yaml @@ -162,9 +162,12 @@ jobs: echo "PR #$existing already open for $UPSTREAM -> $TARGET; nothing to do." exit 0 fi + BODY="Automated sync could not fast-forward \`$TARGET\` from \`$UPSTREAM\` (diverged history or branch protection). Merging via PR instead. + +Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" gh pr create --repo "$REPO" --base "$TARGET" --head "$UPSTREAM" \ --title "sync: $UPSTREAM -> $TARGET" \ - --body "Automated sync could not fast-forward \`$TARGET\` from \`$UPSTREAM\` (diverged history or branch protection). Merging via PR instead." \ + --body "$BODY" \ || echo "No PR created (possibly no diff between $UPSTREAM and $TARGET)." - name: Report From 83bc9d283949cce4598a5531ab869c2d0909f52b Mon Sep 17 00:00:00 2001 From: Nathan Heaps <1282393+nsheaps@users.noreply.github.com> Date: Fri, 29 May 2026 12:03:34 -0400 Subject: [PATCH 3/3] Update sync-main-to-edge.yaml --- .github/workflows/sync-main-to-edge.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-main-to-edge.yaml b/.github/workflows/sync-main-to-edge.yaml index 7d6d35c..c4a6e96 100644 --- a/.github/workflows/sync-main-to-edge.yaml +++ b/.github/workflows/sync-main-to-edge.yaml @@ -162,9 +162,8 @@ jobs: echo "PR #$existing already open for $UPSTREAM -> $TARGET; nothing to do." exit 0 fi - BODY="Automated sync could not fast-forward \`$TARGET\` from \`$UPSTREAM\` (diverged history or branch protection). Merging via PR instead. - -Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + BODY="Automated sync could not fast-forward \`$TARGET\` from \`$UPSTREAM\` (diverged history or branch protection). Merging via PR instead.\ + Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" gh pr create --repo "$REPO" --base "$TARGET" --head "$UPSTREAM" \ --title "sync: $UPSTREAM -> $TARGET" \ --body "$BODY" \