From 5c581eab0774104a51cc5b28b15a6d7457305752 Mon Sep 17 00:00:00 2001 From: Nils <52573120+niStee@users.noreply.github.com> Date: Fri, 7 Aug 2026 01:12:43 +0200 Subject: [PATCH] ci: switch to Dependabot-style handoff (no self-approval) --- .github/workflows/sync-fork.yml | 70 +++++++++++++++------------------ 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/.github/workflows/sync-fork.yml b/.github/workflows/sync-fork.yml index eb2b0b0a3f..40436065e9 100644 --- a/.github/workflows/sync-fork.yml +++ b/.github/workflows/sync-fork.yml @@ -1,29 +1,30 @@ name: Sync Fork with Upstream -# Pull-based fork sync with GitHub App installation token for approval. +# Pull-based fork sync with Dependabot-style handoff. # # Uses `actions/create-github-app-token@v2` to mint a short-lived installation # token per workflow run. The App is scoped to ONLY the repos where it's -# installed — no org-wide or account-wide access. Tokens expire within the -# hour and cannot be reused if exfiltrated. +# installed. Tokens expire within the hour and cannot be reused if exfiltrated. # -# Prerequisites (already configured on this repo): -# - GitHub App named 'fork-sync-approver' (or similar) installed on this repo -# - Repo secret 'APPID4510581' containing the .pem private key -# - Repo secret 'APP_ID' containing just the number 4510581 +# Flow (Dependabot-style): +# 1. App token opens a sync PR (as the App's identity) +# 2. Workflow posts a comment on the PR asking for human review +# 3. You (the repo owner) click Approve + Merge # -# Why this design (vs. PAT / OAuth): -# - GITHUB_TOKEN (github-actions[bot]) cannot self-approve its own PRs -# (GitHub blocks this even for admins via API, HTTP 422) -# - PATs are long-lived, single-tenant, and broad-scope — risky on public -# repos (anyone can read the secret via Actions API or fork PR attacks) -# - OAuth tokens have org-wide scopes — dangerous if leaked -# - GitHub App installation tokens are short-lived, repo-scoped, and -# least-privilege — the correct choice for fork sync automation +# Why no self-approval: +# GitHub blocks any identity from approving its own PR (HTTP 422 "Review +# Can not approve your own pull request"). bypass_actors doesn't bypass this +# rule — it only bypasses the ruleset's "require N approvals" requirement +# for actors that ARE allowed to approve. +# +# Prerequisites: +# - GitHub App 'fork-sync-approver' installed on this repo (App ID 4510581) +# - Repo secret APPID4510581 = .pem private key of the App +# - Repo secret APP_ID = 4510581 (just the number) # # Pattern sources (verified Aug 2026): # - agent-of-empires #1420 (pull-based) -# - ConductionNL #61 (PAT-approver pattern; we use App instead of PAT) +# - ConductionNL #61 (PAT-approver pattern; we use Dependabot-style handoff instead) # - bluesky-social/social-app PR #11010 (App-based workflow approval) on: @@ -87,6 +88,7 @@ jobs: run: | DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" SYNC_BRANCH="sync/upstream-$(date -u +%Y%m%d-%H%M%S)" + # Create the sync branch on the fork, pointing at upstream's HEAD gh api -X POST "repos/${{ github.repository }}/git/refs" \ -f ref="refs/heads/$SYNC_BRANCH" \ -f sha="${{ steps.upstream.outputs.upstream_head }}" \ @@ -100,37 +102,29 @@ jobs: --title "Sync fork with $PARENT:$UPSTREAM_DEFAULT" \ --body "Automated pull-based sync from [$PARENT](https://github.com/$PARENT). - Uses GitHub App installation token for short-lived, repo-scoped approval (actions/create-github-app-token@v2).") + **Handoff to human:** GitHub blocks any identity from approving its own PR, so the workflow cannot auto-approve. Please click **Approve** then **Merge** when you're ready. + + *(This is the Dependabot-style pattern: the workflow opens the PR, you approve and merge. No credentials required.)*) + Automated sync uses GitHub App installation token for PR creation only.") PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$') echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT echo "url=$PR_URL" >> $GITHUB_OUTPUT echo "branch=$SYNC_BRANCH" >> $GITHUB_OUTPUT echo "::notice::Opened sync PR #$PR_NUMBER: $PR_URL" - - name: Approve PR via GitHub App installation token + - name: Post handoff comment requesting review if: steps.upstream.outputs.sync_needed == 'true' env: GH_TOKEN: ${{ steps.app_token.outputs.token }} run: | PR_NUMBER="${{ steps.pr.outputs.number }}" - if gh api -X POST "repos/${{ github.repository }}/pulls/$PR_NUMBER/reviews" \ - -f event="APPROVE" \ - -f body="Auto-approved by Sync Fork workflow (GitHub App installation token)."; then - echo "::notice::Approved PR #$PR_NUMBER via GitHub App token." - else - echo "::error::Failed to approve PR #$PR_NUMBER." - exit 1 - fi + # Post a comment tagging the repo owner so they get a notification + gh pr comment "$PR_NUMBER" --repo "${{ github.repository }}" --body "👋 **Sync ready for review** @niStee — this PR syncs upstream changes into this fork. - - name: Merge PR (all ruleset requirements satisfied) - if: steps.upstream.outputs.sync_needed == 'true' - env: - GH_TOKEN: ${{ steps.app_token.outputs.token }} - run: | - PR_NUMBER="${{ steps.pr.outputs.number }}" - if gh pr merge "$PR_NUMBER" --repo "${{ github.repository }}" --squash --delete-branch; then - echo "::notice::Merged sync PR #$PR_NUMBER." - else - echo "::error::Failed to merge PR #$PR_NUMBER — check for required status checks or other ruleset rules." - exit 1 - fi + **To merge:** + 1. Wait for CI checks to pass (if any) + 2. Click **Approve** + 3. Click **Merge** + + _Why manual approval? GitHub blocks any identity from approving its own PR (HTTP 422). The workflow can't auto-approve itself, so this is the standard Dependabot-style handoff pattern: workflow opens, you merge._" + echo "::notice::Posted review request comment on PR #$PR_NUMBER"