Repository navigation
feat: push the formatter's commit from a GitHub App - #7
Conversation
A commit pushed by GITHUB_TOKEN starts no workflow run, so the auto-format left the acls check stale and asked the author to push again. A GitHub App's push does start runs, so the fix becomes self-completing. Configuration is optional and detected at runtime: job-level env reads the secrets (which `secrets` is available in) and the steps branch on it (which `env` is available in). With FMT_APP_ID and FMT_APP_PRIVATE_KEY set, the job mints a 1h installation token and checks out with it; without them it pushes with GITHUB_TOKEN exactly as before and keeps telling the author how to get a fresh run. This can therefore merge before the app exists. The app only needs contents:write -- comments and PR reads still go through GITHUB_TOKEN, which also keeps the loop guard (sender != github-actions[bot]) correct, since the app never comments. On the app path the "Rewrite the lint report" step is skipped: the push triggers a real run whose own report job rewrites the comment, and doing it here too would race that run. README carries the setup steps and notes that the private key is the one durable credential in the repo, scoped to contents:write on this repo alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Confirms the app can mint an installation token for this repo before lint-fix.yml relies on it. Delete once confirmed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Observed on #6: the bot's GITHUB_TOKEN push did create a pull_request run on the formatted commit (35367741574), but it sat from 16:19:01 to 16:20:12 until it was approved by hand. That is the documented third exception -- a workflow that updates a pull request does create opened/synchronize/reopened runs, in an approval-required state -- so the previous wording, that the push starts no run at all and the author should push an empty commit or reopen the PR, was wrong. The fallback reply now says the run is waiting for approval and who can clear it. The note that re-running the old check doesn't help stays, since a re-run still reuses the original unformatted commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ Policy lint passed
Lint output
|
The smoke test on this branch returned 404 from get-a-repository-installation-for-the-authenticated-app: the app id and key are valid (a bad key gives 401) but the app has no installation on this repo. With the mint step required, that would have taken the whole fmt job down and broken /fmt entirely -- worse than the approval click it was meant to remove. The mint step is now continue-on-error, and the steps after it branch on the token actually obtained rather than on the secrets being configured, so a missing installation or a rotated-out key degrades to the GITHUB_TOKEN path instead of failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The smoke test did its job — it failed, and usefully. A bad key or wrong app id returns 401; this is 404, which means the JWT was accepted and the app simply has no installation on this repository. So That failure also exposed a worse problem than the one this PR set out to fix, now addressed in 4a846e9: with the mint step required, a missing installation took the whole Once the app is installed, the smoke test should print the repo it can reach. Then f9b7aa8 and On approving the held run programmatically instead: not viable. |
Confirmed on rerun 35370080108 after the app was installed: installation 162802390, token scoped to Comet-Robotics/tailscale-acls alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
App is installed and the token works. Rerun
Smoke test removed in 98a5e54. This PR is now just the two files, and Two hardening suggestions, neither blocking
Both are changed in the app's settings; GitHub may ask you to accept the change on the installation. Worth doing since the private key is now the one durable credential in a repo that gates tailnet access, but the design works either way. Still unverifiedThe smoke test proved the token can be minted. It didn't prove the app-token push produces a run that isn't held for approval — that's the whole point of the change, and it can only be tested once (Unrelated log noise: |
Follow-up to #5. Makes the auto-format complete without a human clicking Approve and run, and corrects the wording #5 shipped with.
What #6 taught us
The checkbox flow worked end to end on #6 (run
35367718975: write-perm check → resolve branch → checkout → format → commitaa41e97→ rewrite comment → reply;Acknowledge the requestskipped, confirming it came from the checkbox rather than/fmt; the follow-up run35368016195correctly skipped as the loop guard).But #5's README was wrong about what happens next. The bot's
GITHUB_TOKENpush did create apull_requestrun on the formatted commit — run35367741574,created_at 16:19:01,run_started_at 16:20:12— it just sat there until it was approved by hand. That's the documented third exception: a workflow that updates a pull request does createopened/synchronize/reopenedruns, in an approval-required state. So "the push starts no workflow run, go push an empty commit or reopen the PR" was simply wrong, and e827349 replaces it.What this adds
lint-fix.ymlmints a GitHub App installation token and pushes with that. An app's push isn't held for approval, so theaclscheck re-runs on the formatted commit by itself.It's optional and detected at runtime — job-level
envreads the secrets, steps branch on it (both context-availability rules checked against the docs):FMT_APP_ID+FMT_APP_PRIVATE_KEYset → app token pushes, run starts immediately, and that run's ownreportjob rewrites the comment.GITHUB_TOKENas before, rewrites the comment itself, and the reply now says the run is waiting for approval and who can clear it.The app needs
contents:writeand nothing else; comments and PR reads still go throughGITHUB_TOKEN, which keeps the loop guard (sender != github-actions[bot]) correct since the app never comments.The temporary commit
f9b7aa8 adds
.github/workflows/tmp-app-smoke.yml, which just mints a token and prints which repos it can reach. It must come out before merge. It's here because the app's installation isn't confirmed:orgs/Comet-Robotics/installationslists 6 apps (cursor, draw-io-app, gitbook-com, sentry, trylle, vercel) and no formatter, which suggests the app was created but perhaps not installed on the org — and if there's no installation,create-github-app-tokenfails hard and takes thefmtjob with it. The smoke test turns that into a clear yes/no before anything depends on it. It prints no secret.Not done, deliberately
Auto-approving the held run instead of using an app is not viable:
POST /actions/runs/{run_id}/approveis scoped to "a pull request from a public fork of a first time contributor," not to runs held becauseGITHUB_TOKENpushed them.🤖 Generated with Claude Code