Skip to content

Skip revoked GitHub tokens and queue one Satis build per release - #543

Merged
simonhamp merged 1 commit into
mainfrom
fix-missing-plugin-dists-link-1
Sep 29, 2026
Merged

simonhamp merged 1 commit into
mainfrom
fix-missing-plugin-dists-link-1

Conversation

@simonhamp

Copy link
Copy Markdown
Member

This fixes two problems in how plugin releases reach the Satis server.

The token resolver handed out whatever token it had stored without trying it. An installation token stops working when the app is uninstalled or loses access to a repo. A user can also revoke our access. We don't always hear about either, and a revoked token fails everything it's passed to. For a paid plugin that includes the Satis build. Satis then can't archive the package, and the version's dist is left pointing at the GitHub zipball, which our users can't download from a private repo.

The resolver now calls GET /repos/{owner}/{repo} with the installation token, then the owner's token, and moves on to the next one if GitHub answers 401 or 404. Any other failure, like a rate limit or GitHub being down, doesn't count against a token because it says nothing about the token itself. The platform token is the last resort, so it isn't tried. PluginSyncService had its own copy of the resolver and now uses the shared one. SyncPluginReleases used to resolve a token twice and now builds with the one it fetched releases with.

Since the move to the GitHub App, release events can arrive twice: once through the old per-repo webhook and once through the App webhook. GitHub also sends created, published and released for a single publish, and neither handler checked the action, so one publish could queue up to six identical builds. Nightwatch issues 1 and 6 on the plugins app show builds have already overlapped in the shared build directory. Both handlers now go through Plugin::claimReleaseEvent(). It only lets published through, and the first delivery claims the release in the cache for an hour, so the second webhook's copy is ignored.

The per-repo handler never read the payload before, so it worked whatever content type the webhook used. GitHub's add-webhook form defaults to form data and our instructions to switch it to JSON are easy to miss, so the handler now reads the payload either way.

Things to watch for:

  • Promoting a pre-release to a full release, or editing or deleting a release, no longer triggers a sync. latest_version catches up on the next push.
  • Resolving a token now costs a GitHub API call for each stored token it tries.
  • This cuts the duplicate builds but doesn't stop builds overlapping on the satis server, since buildAll() still sends one build per plugin back to back. That needs WithoutOverlapping on the satis jobs in the plugins repo.
  • Push events still arrive through both webhooks. They only sync metadata, so they're unchanged here.

New tests are in SatisBuildTokenTest, PluginWebhookTest, GitHubAppRepositoryEventsTest and PluginSyncServiceTest. The eight that cover the new behaviour fail against the old code, and the full suite passes.

🤖 Generated with Claude Code

The token resolver handed out stored tokens without checking them, so a
revoked installation or user token failed everything it was passed to,
including the Satis build. It now tries each one against the plugin's repo
and moves on after a 401 or 404. PluginSyncService drops its own copy of
the resolver and uses the shared one.

Both webhook handlers now only act on the "published" release action, and
the first delivery of a release claims it, so the per-repo webhook and the
GitHub App webhook no longer queue a build each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simonhamp
simonhamp marked this pull request as ready for review September 29, 2026 15:06
@simonhamp
simonhamp merged commit 7ecfe8b into main Sep 29, 2026
3 checks passed
@simonhamp
simonhamp deleted the fix-missing-plugin-dists-link-1 branch September 29, 2026 15:07
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.

1 participant