From 3f716c338123a37d9cfaaf8e425176061c630988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Rubinstein?= Date: Tue, 10 Feb 2026 11:22:09 +0100 Subject: [PATCH 1/4] Recommend GitHub App token to fix CI not triggering on upstack PRs Pushes made with the default GITHUB_TOKEN don't trigger workflow runs (GitHub's infinite-loop prevention). This means CI never runs on upstack PRs after autorestack pushes the synthetic merge commit, so they can't become mergeable if branch protection requires status checks. The setup instructions now walk users through creating a GitHub App and using actions/create-github-app-token to get an installation token. The old GITHUB_TOKEN approach is preserved in a collapsed section for repos that don't need CI on upstack PRs. Co-Authored-By: Claude Opus 4.6 --- README.md | 42 +++++++++++++++++++++++++++++++++++++++++- action.yml | 6 +++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 72eb228..6066fe8 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,50 @@ The action manages branch deletion itself. GitHub's auto-delete setting must be gh api -X PATCH "/repos/OWNER/REPO" --input - <<< '{"delete_branch_on_merge":false}' ``` -**2. Add the workflow** +**2. Create a GitHub App** + +When autorestack pushes the synthetic merge commit to upstack branches, you probably want CI to run on those PRs so they can become mergeable. Pushes made with the default `GITHUB_TOKEN` [do not trigger workflow runs](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) — this is a deliberate GitHub limitation to prevent infinite loops. A GitHub App installation token does not have this limitation. + +1. [Create a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) with the following repository permissions: + - **Contents:** Read and write (to push branches) + - **Pull requests:** Read and write (to update PRs, add labels, post comments) +2. Install the app on your repository +3. Store the App ID in a repository variable (e.g. `AUTORESTACK_APP_ID`) +4. Generate a private key and store it in a repository secret (e.g. `AUTORESTACK_PRIVATE_KEY`) + +**3. Add the workflow** Create a `.github/workflows/update-pr-stack.yml` file: ```yaml name: Update PR Stack +on: + pull_request: + types: [closed, synchronize] + +jobs: + update-pr-stack: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.AUTORESTACK_APP_ID }} + private-key: ${{ secrets.AUTORESTACK_PRIVATE_KEY }} + + - uses: Phlogistique/autorestack-action@main + with: + github-token: ${{ steps.app-token.outputs.token }} +``` + +
+Using GITHUB_TOKEN instead (CI won't trigger on upstack PRs) + +If you don't need CI checks on upstack PRs — for example, if your repository has no branch protection rules requiring status checks — you can use the default token: + +```yaml +name: Update PR Stack + on: pull_request: types: [closed, synchronize] @@ -80,6 +118,8 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} ``` +
+ ### Notes * Currently only supports squash merges diff --git a/action.yml b/action.yml index aab3651..4eb9363 100644 --- a/action.yml +++ b/action.yml @@ -4,7 +4,11 @@ author: 'GitHub Actions' inputs: github-token: - description: 'GitHub token for API access' + description: > + Token for git push and GitHub API calls. The default GITHUB_TOKEN will NOT + trigger CI on upstack PRs (GitHub deliberately suppresses workflow runs from + pushes made with that token). Use a GitHub App installation token if you need + checks to run on the updated PRs. required: true default: ${{ github.token }} From 7c89daa551292c647aaf55ef3ddb3153da1d79f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Rubinstein?= Date: Tue, 10 Feb 2026 11:37:54 +0100 Subject: [PATCH 2/4] Use actions/create-github-app-token in CI instead of custom Python script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dogfood the same approach we recommend to users. Removes the uv dependency from CI. The Python script stays for local dev use via run-e2e-tests.sh. The secret changes from GH_APP_PRIVATE_KEY_PEM_B64 (base64-encoded) to GH_APP_PRIVATE_KEY (raw PEM) — needs a corresponding update in the repo settings. Co-Authored-By: Claude Opus 4.6 --- .claude/run-e2e-tests.sh | 4 ++-- .github/workflows/tests.yml | 12 ++++++------ tests/get_github_app_token.py | 12 ++++-------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.claude/run-e2e-tests.sh b/.claude/run-e2e-tests.sh index 444bf11..05f45ea 100755 --- a/.claude/run-e2e-tests.sh +++ b/.claude/run-e2e-tests.sh @@ -63,8 +63,8 @@ acquire_token() { log_info "Acquiring GitHub App token..." # Check if required environment variables are set - if [[ -z "$GH_APP_ID" ]] || [[ -z "$GH_APP_PRIVATE_KEY_PEM_B64" ]]; then - log_error "Missing required environment variables: GH_APP_ID and/or GH_APP_PRIVATE_KEY_PEM_B64" + if [[ -z "$GH_APP_ID" ]] || [[ -z "$GH_APP_PRIVATE_KEY" ]]; then + log_error "Missing required environment variables: GH_APP_ID and/or GH_APP_PRIVATE_KEY" return 1 fi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6cf395c..bce0d1b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,17 +24,17 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install uv - uses: astral-sh/setup-uv@v4 + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v2 with: - version: "latest" + app-id: ${{ vars.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - name: Run e2e tests env: - GH_APP_ID: ${{ vars.GH_APP_ID }} - GH_APP_PRIVATE_KEY_PEM_B64: ${{ secrets.GH_APP_PRIVATE_KEY_PEM_B64 }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} PRESERVE_ON_FAILURE: "1" run: | - export GH_TOKEN=$(uv run tests/get_github_app_token.py) gh auth setup-git bash tests/test_e2e.sh diff --git a/tests/get_github_app_token.py b/tests/get_github_app_token.py index 62dcf13..4eacd03 100755 --- a/tests/get_github_app_token.py +++ b/tests/get_github_app_token.py @@ -15,7 +15,7 @@ Environment variables required: - GH_APP_ID: GitHub App ID -- GH_APP_PRIVATE_KEY_PEM_B64: Base64-encoded private key +- GH_APP_PRIVATE_KEY: PEM private key Usage: # Run with uv (automatically installs dependencies) @@ -28,7 +28,6 @@ export GITHUB_TOKEN=$(uv run get_github_app_token.py) """ -import base64 import json import os import sys @@ -99,15 +98,12 @@ def generate_installation_token(): """Generate a new installation access token for the GitHub App.""" # Get credentials from environment app_id = os.getenv("GH_APP_ID") - private_key_b64 = os.getenv("GH_APP_PRIVATE_KEY_PEM_B64") + private_key = os.getenv("GH_APP_PRIVATE_KEY") - if not all([app_id, private_key_b64]): - print("Error: Missing GH_APP_ID or GH_APP_PRIVATE_KEY_PEM_B64", file=sys.stderr) + if not all([app_id, private_key]): + print("Error: Missing GH_APP_ID or GH_APP_PRIVATE_KEY", file=sys.stderr) sys.exit(1) - # Decode private key - private_key = base64.b64decode(private_key_b64).decode('utf-8') - # Generate JWT now = int(time.time()) payload = { From 6187253fa0fe5234fee2da1d3532424ebf698c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Rubinstein?= Date: Tue, 10 Feb 2026 12:28:23 +0100 Subject: [PATCH 3/4] Trigger CI with new secret From 8f1490be5c194ef559e5b7520e09132760e56c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Rubinstein?= Date: Tue, 10 Feb 2026 17:55:57 +0100 Subject: [PATCH 4/4] Trigger CI after re-setting secret --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bce0d1b..0607bc0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,7 @@ jobs: with: app-id: ${{ vars.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: autorestack-test - name: Run e2e tests env: