Skip to content

Commit 82b89d9

Browse files
Phlogistiqueclaude
andauthored
Recommend GitHub App token for CI to trigger on upstack PRs (#27)
* 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 <noreply@anthropic.com> * Use actions/create-github-app-token in CI instead of custom Python script 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 <noreply@anthropic.com> * Trigger CI with new secret * Trigger CI after re-setting secret --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8dc0ac1 commit 82b89d9

5 files changed

Lines changed: 59 additions & 18 deletions

File tree

.claude/run-e2e-tests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ acquire_token() {
6363
log_info "Acquiring GitHub App token..."
6464

6565
# Check if required environment variables are set
66-
if [[ -z "$GH_APP_ID" ]] || [[ -z "$GH_APP_PRIVATE_KEY_PEM_B64" ]]; then
67-
log_error "Missing required environment variables: GH_APP_ID and/or GH_APP_PRIVATE_KEY_PEM_B64"
66+
if [[ -z "$GH_APP_ID" ]] || [[ -z "$GH_APP_PRIVATE_KEY" ]]; then
67+
log_error "Missing required environment variables: GH_APP_ID and/or GH_APP_PRIVATE_KEY"
6868
return 1
6969
fi
7070

.github/workflows/tests.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@ jobs:
2424
- name: Checkout repository
2525
uses: actions/checkout@v4
2626

27-
- name: Install uv
28-
uses: astral-sh/setup-uv@v4
27+
- name: Generate GitHub App token
28+
id: app-token
29+
uses: actions/create-github-app-token@v2
2930
with:
30-
version: "latest"
31+
app-id: ${{ vars.GH_APP_ID }}
32+
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
33+
owner: autorestack-test
3134

3235
- name: Run e2e tests
3336
env:
34-
GH_APP_ID: ${{ vars.GH_APP_ID }}
35-
GH_APP_PRIVATE_KEY_PEM_B64: ${{ secrets.GH_APP_PRIVATE_KEY_PEM_B64 }}
37+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
3638
PRESERVE_ON_FAILURE: "1"
3739
run: |
38-
export GH_TOKEN=$(uv run tests/get_github_app_token.py)
3940
gh auth setup-git
4041
bash tests/test_e2e.sh

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,50 @@ The action manages branch deletion itself. GitHub's auto-delete setting must be
5757
gh api -X PATCH "/repos/OWNER/REPO" --input - <<< '{"delete_branch_on_merge":false}'
5858
```
5959

60-
**2. Add the workflow**
60+
**2. Create a GitHub App**
61+
62+
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.
63+
64+
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:
65+
- **Contents:** Read and write (to push branches)
66+
- **Pull requests:** Read and write (to update PRs, add labels, post comments)
67+
2. Install the app on your repository
68+
3. Store the App ID in a repository variable (e.g. `AUTORESTACK_APP_ID`)
69+
4. Generate a private key and store it in a repository secret (e.g. `AUTORESTACK_PRIVATE_KEY`)
70+
71+
**3. Add the workflow**
6172

6273
Create a `.github/workflows/update-pr-stack.yml` file:
6374
```yaml
6475
name: Update PR Stack
6576

77+
on:
78+
pull_request:
79+
types: [closed, synchronize]
80+
81+
jobs:
82+
update-pr-stack:
83+
runs-on: ubuntu-latest
84+
steps:
85+
- uses: actions/create-github-app-token@v2
86+
id: app-token
87+
with:
88+
app-id: ${{ vars.AUTORESTACK_APP_ID }}
89+
private-key: ${{ secrets.AUTORESTACK_PRIVATE_KEY }}
90+
91+
- uses: Phlogistique/autorestack-action@main
92+
with:
93+
github-token: ${{ steps.app-token.outputs.token }}
94+
```
95+
96+
<details>
97+
<summary>Using <code>GITHUB_TOKEN</code> instead (CI won't trigger on upstack PRs)</summary>
98+
99+
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:
100+
101+
```yaml
102+
name: Update PR Stack
103+
66104
on:
67105
pull_request:
68106
types: [closed, synchronize]
@@ -80,6 +118,8 @@ jobs:
80118
github-token: ${{ secrets.GITHUB_TOKEN }}
81119
```
82120
121+
</details>
122+
83123
### Notes
84124
85125
* Currently only supports squash merges

action.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ author: 'GitHub Actions'
44

55
inputs:
66
github-token:
7-
description: 'GitHub token for API access'
7+
description: >
8+
Token for git push and GitHub API calls. The default GITHUB_TOKEN will NOT
9+
trigger CI on upstack PRs (GitHub deliberately suppresses workflow runs from
10+
pushes made with that token). Use a GitHub App installation token if you need
11+
checks to run on the updated PRs.
812
required: true
913
default: ${{ github.token }}
1014

tests/get_github_app_token.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
1616
Environment variables required:
1717
- GH_APP_ID: GitHub App ID
18-
- GH_APP_PRIVATE_KEY_PEM_B64: Base64-encoded private key
18+
- GH_APP_PRIVATE_KEY: PEM private key
1919
2020
Usage:
2121
# Run with uv (automatically installs dependencies)
@@ -28,7 +28,6 @@
2828
export GITHUB_TOKEN=$(uv run get_github_app_token.py)
2929
"""
3030

31-
import base64
3231
import json
3332
import os
3433
import sys
@@ -99,15 +98,12 @@ def generate_installation_token():
9998
"""Generate a new installation access token for the GitHub App."""
10099
# Get credentials from environment
101100
app_id = os.getenv("GH_APP_ID")
102-
private_key_b64 = os.getenv("GH_APP_PRIVATE_KEY_PEM_B64")
101+
private_key = os.getenv("GH_APP_PRIVATE_KEY")
103102

104-
if not all([app_id, private_key_b64]):
105-
print("Error: Missing GH_APP_ID or GH_APP_PRIVATE_KEY_PEM_B64", file=sys.stderr)
103+
if not all([app_id, private_key]):
104+
print("Error: Missing GH_APP_ID or GH_APP_PRIVATE_KEY", file=sys.stderr)
106105
sys.exit(1)
107106

108-
# Decode private key
109-
private_key = base64.b64decode(private_key_b64).decode('utf-8')
110-
111107
# Generate JWT
112108
now = int(time.time())
113109
payload = {

0 commit comments

Comments
 (0)