Keep GitHub connections in step with app installs and uninstalls
Merged
AR
7d ago+1152+1152+0123456789012345678901234567890123456789-30-30-01234567890123456789
Problem
A customer uninstalled the GitHub App and installed it again twice. Responder kept all three connections marked connected, each with its own copy of the same repository. The repository picker listed konzeptlab/gemhog three times. The automation still pointed at the repository from the first, removed installation, so triage runs failed with "Unable to create GitHub installation token".
Causes:
- The webhook ignored
installationevents, so an uninstall never reached the connection. - Connections are keyed by installation ID. GitHub creates a new installation ID on every reinstall, so each reinstall created a new connection and new repository records.
Changes
- Reinstall reuses the connection. A new installation for a GitHub account that is already connected (matched by GitHub account ID) takes over that connection. Repository record IDs stay the same, so automations keep working. If other connections to the same GitHub account exist, their automation, agent, pull request trigger, and scan references move to the kept connection (repositories matched by GitHub ID), and then those connections are removed. GitHub allows only one installation of an app per account, so those installations no longer exist. The callback lists repositories before it changes any connection.
- Installation webhooks.
installation.deletedremoves the connection.suspendmarks it failed and hides its repositories.unsuspend,new_permissions_accepted, andinstallation_repositoriesresync repositories, and a resync that finds the installation gone removes it. Each case then pauses agents that lost a repository. A failure on one connection does not stop the others. - Removal rule. A removed installation is deleted when no active automation or agent uses it. References from superseded automation versions are deleted with it, as
deleteIntegrationAccountalready does. If something active still uses it, it is kept and markederror, its repositories become unavailable, and the Settings card shows Reconnect. Reinstalling restores it. - Repository refresh. A 404 from GitHub now removes that connection instead of failing the refresh for every connection. Agents that lost a repository are paused even when another connection's refresh fails.
- Run error. A 404 on the token request raises
GitHubInstallationNotFoundError: "The GitHub App is no longer installed. Reconnect GitHub in Settings."
Testing
pnpm typecheck,pnpm lint,pnpm testpass.- New unit tests cover the upsert, removal, webhook handling, refresh, the callback, and the token error.
- I ran
upsertGitHubInstallationAccountandremoveGitHubInstallationAccountagainst a migrated Postgres 17 database. The run covered uninstalling while in use (row markederror), reinstalling (same row and repository IDs, new installation ID), removing a leftover duplicate that only a superseded version referenced, and deleting once nothing used the row.
Summary by cubic
Keeps GitHub connections in step with app installs and uninstalls. Previously, uninstalling the GitHub App left its connection marked connected, and reinstalling created a duplicate connection with its own repository records, leaving stale repository listings and failing automations.
Changes
- Installation webhooks remove uninstalled connections, mark suspended ones failed with repositories unavailable, and resync repositories when access changes.
- Reinstalling reuses the existing connection for the GitHub account, moving automation, agent, trigger, and scan references onto it and keeping its repository records; leftover duplicates are removed.
- The connect callback fetches repositories before changing connections, so an installation that disappears mid-connection changes nothing.
- A removed installation is deleted when no active automation or agent uses it; otherwise it stays failed with repositories unavailable and Settings asks to reconnect.
- Repository refresh removes a connection on a 404 instead of failing the whole refresh, and one failing connection no longer stops the others; affected agents are paused even when another refresh fails.
- Runs on a removed installation report "Reconnect GitHub in Settings."