From d03d4493bd772b14568fd163457f3371b84fab1f Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Fri, 1 May 2026 11:50:38 +0200 Subject: [PATCH] fix(blog-autopublish): use App-minted token instead of GITHUB_TOKEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Org policy "Allow GitHub Actions to create and approve pull requests" is disabled, which silently kills `gh pr create` calls authed with GITHUB_TOKEN. The cron has been failing for two days running (#43, #45) with "GitHub Actions is not permitted to create or approve pull requests" — three scheduled posts (overdoing-the-verification-chain, variant-pruning-rust-mcdc, cross-language-lto) sat as drafts. Mint a token from the PulseEngine Actions Helper App (actions/create-github-app-token@v1) using the ACTIONS_BOT_APP_ID and ACTIONS_BOT_PRIVATE_KEY repository secrets. The App's installation grants Contents R/W + Pull requests R/W + Metadata R on this repo only — strictly narrower than what the org-disabled GITHUB_TOKEN would have had. App-minted tokens authenticate as the App, not as Actions, so the org-level restriction stays intact for the default token while this single workflow has the explicit grant it needs. Five surgical changes: - New "Mint App token" step before checkout - actions/checkout@v4 takes the App token (so credential helper uses it for `git push` later) - Four env: GH_TOKEN swaps (Ensure labels exist, Publish ready posts, Post / update status comment, Open failure issue) The existing permissions: block (contents/pull-requests/issues: write) stays as-is for clarity / defense in depth, even though we no longer use the default GITHUB_TOKEN in this job. Token expires in 1 hour; workflow timeout is 15 min, so no rotation logic needed. Trigger workflow_dispatch after merge to flush the three stuck posts in one run. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/blog-autopublish.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/blog-autopublish.yml b/.github/workflows/blog-autopublish.yml index 37eb3b4..1941da1 100644 --- a/.github/workflows/blog-autopublish.yml +++ b/.github/workflows/blog-autopublish.yml @@ -46,9 +46,23 @@ jobs: RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} REPO: ${{ github.repository }} steps: + # Mint a token from the PulseEngine Actions Helper App so this job + # can `gh pr create` despite the org policy that disables PR-creation + # for the default GITHUB_TOKEN. The App's installation grants + # contents: write + pull-requests: write + metadata: read on this + # repo only — strictly narrower than what GITHUB_TOKEN would have + # had with the org permission flipped. + - name: Mint App token + id: app_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.ACTIONS_BOT_APP_ID }} + private-key: ${{ secrets.ACTIONS_BOT_PRIVATE_KEY }} + - uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ steps.app_token.outputs.token }} - uses: actions/setup-python@v5 with: @@ -61,7 +75,7 @@ jobs: - name: Ensure labels exist env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} run: | # `gh label create --force` upserts (creates or updates), so this # step is idempotent and immune to a label being deleted manually. @@ -91,7 +105,7 @@ jobs: id: publish if: steps.scan.outputs.ready_count != '0' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} run: | set -euo pipefail published='[]' @@ -146,7 +160,7 @@ jobs: - name: Post / update status comment if: always() env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} run: | set -euo pipefail @@ -179,7 +193,7 @@ jobs: - name: Open failure issue if: failure() env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app_token.outputs.token }} run: | today=$(date -u +%Y-%m-%d) gh issue create \