Skip to content

ci(release): open the appcast PR as a GitHub App and auto-merge it - #64

Open
alecf wants to merge 1 commit into
mainfrom
claude/release-workflow-automation-xml0wb
Open

ci(release): open the appcast PR as a GitHub App and auto-merge it#64
alecf wants to merge 1 commit into
mainfrom
claude/release-workflow-automation-xml0wb

Conversation

@alecf

@alecf alecf commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Cuts the release down to a single gh workflow run release.yml with no manual clicks after it.

The problem

release.yml opened the release/vX.Y.Z PR with the default GITHUB_TOKEN. GitHub starts no workflow run for events created with that token — it's platform-level recursion protection, not a permissions setting — so the PR arrived with no CI at all and its required checks stayed unfilled until someone pushed an empty commit or closed and reopened it.

On #63 that was 41 minutes of dead time: PR opened 22:33, checks finally started 23:14.

The fix

Mint a short-lived installation token from a dedicated GitHub App and use it to push the branch and open the PR. An app is a distinct identity, so ci.yml, pr-title.yml and pr-screenshot.yml all fire the moment the PR opens — which is what makes auto-merge viable, since auto-merge needs real pending checks to wait on.

Phase 2 is unchanged: the merge still triggers release-publish.yml to flip the draft Release to published, and deploy-site.yml to push the appcast to Pages. Both fire correctly because an app-driven merge is not a GITHUB_TOKEN event either.

⚠️ Required before merging

The next release dispatch fails at step one without these. Setup is written up in docs/sparkle-setup.md § 4.

  1. Create a personal GitHub App (Settings → Developer settings → GitHub Apps), webhook off, repository permissions limited to Contents: Read and write and Pull requests: Read and write, installable only on this account. Install it on totalrecall.
  2. Add two repository secrets:
    gh secret set RELEASE_APP_ID --body "<app id>"
    gh secret set RELEASE_APP_PRIVATE_KEY < <app>.private-key.pem
    
  3. Turn on Settings → General → Allow auto-merge.

main already requires status checks, which auto-merge needs — it can only be requested on a PR that isn't already mergeable. If main also requires an approving review, the release PR waits for yours; since the app authors it, you're free to approve it yourself.

Notes for review

  • Fails fast. The credential check is the first step in the job, so a missing or rotated secret costs seconds instead of a full build-sign-package cycle.
  • Degrades rather than breaks. If auto-merge is off at the repo level, the workflow warns and leaves a normal PR to merge by hand — it doesn't fail the release.
  • Auto-merge retries. A just-created PR briefly reads as already-mergeable and GitHub rejects auto-merge on a clean PR, so the request retries 6× at 10s intervals while the checks register.
  • Re-dispatch bug fixed along the way. The old open-PR guard used gh pr view "$BRANCH", which also resolves a closed PR. Aborting a release and re-running would have skipped PR creation and left the re-dispatch with nothing mergeable. Now scoped to open PRs.
  • Commit attribution. The appcast commit is authored by the app's bot user, which needs the numeric user id in the noreply address — looked up from the app slug, since the token output doesn't carry it.
  • permissions: narrowed to contents: write; pull-requests: write is no longer needed on GITHUB_TOKEN now that the app handles the PR.
  • actions/create-github-app-token is SHA-pinned with a version comment, matching the other actions in this repo.

Docs

CLAUDE.md, docs/sparkle-setup.md and README.md updated. The sparkle-setup rewrite covers the app creation steps and both repo settings; CLAUDE.md carries a warning against "simplifying" the app token back to GITHUB_TOKEN, which would silently take the checks and auto-merge with it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J2AcWaTJVfuVboe2tXgih9

Screenshot

Total Recall main window

Auto-captured on macos-26 at 17e545c (workflow run). Updated on every push to this PR.

The release PR was opened with the default GITHUB_TOKEN, and GitHub
starts no workflow run for events created with that token. The PR
therefore arrived with no CI at all and its required checks sat unfilled
until someone pushed an empty commit or closed and reopened it — 41
minutes of dead time on the last release.

Mint an installation token from a dedicated GitHub App instead. That is a
distinct identity, so ci.yml, pr-title.yml and pr-screenshot.yml all fire
when the PR opens, which in turn gives auto-merge real checks to wait on.
The release now needs one dispatch and no further clicks.

The auto-merge request retries across the window where a freshly created
PR still reads as mergeable, and degrades to a warning rather than a
failed release if the repository has auto-merge switched off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J2AcWaTJVfuVboe2tXgih9
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Test Coverage

Coverage after merging claude/release-workflow-automation-xml0wb into main will be
90.64%▾ -0.52%
Coverage Report for Changed Files
FileStmtsBranchesFuncsLinesUncovered Lines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants