Skip to content

feat(github): Support every GitHub App installation - #2405

Open
sentry-junior[bot] wants to merge 9 commits into
mainfrom
fix/github-all-installations
Open

sentry-junior[bot] wants to merge 9 commits into
mainfrom
fix/github-all-installations

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

The GitHub plugin only worked with the one installation in GITHUB_INSTALLATION_ID. Repositories under other installations of the same App, such as fairsource/fair.io, were silently dropped: webhooks returned 202 Ignored, and every token was minted for the getsentry installation.

This removes the single-installation rule as a hard cutover. A GitHub App has at most one installation per account, so Junior routes each request to the installation on the account that owns its target.

  • Webhooks accept a signed delivery from any installation of the App. All follow-up requests for that delivery (PR commits, opener/author filters, check suites, reactions) use the installation.id from the signed payload. A delivery with no installation is still ignored.
  • Stored installations: a new plugin-owned table, junior_github_installations (migration 0010_installations), maps each account login to its installation id.
    • installation webhooks save the row on install, permission changes, and unsuspend, and remove it on uninstall and suspend. installation_target webhooks update the login after an account rename.
    • When a lookup finds no row, Junior syncs the table from GET /app/installations, at most once a minute. This fills the table on first use and repairs missed webhooks. Suspended installations are skipped.
    • GET /app (the App owner) is cached for an hour.
  • Agent and host egress grants carry an owner:<login> lease scope. The owner comes from the URL (/repos/{owner}/..., /orgs/{org}/..., git URLs), from the github.*:owner/repo operation that host tools send, or from the GraphQL owner variable that gh sends. Remembered egress leases are keyed per scope, so a token for one installation is never reused for another. One token serves every repository of that account.
  • Host tools that read by node id, such as the review-thread lookup in github_resolvePullRequestReviewThread, now name owner/repo in their operation, so they work for private repositories in other installations.
  • Writes to an account without the App fail with The GitHub App is not installed on <owner>. They never fall back to another installation.
  • Reads that name no owner (search, GraphQL by node id only), or that target an account without the App, use the installation on the App owner's account. That keeps public repositories readable.
  • Config: GITHUB_INSTALLATION_ID and installationIdEnv are gone. Only the App ID, private key, and webhook secret remain. Docs, .env.example files, tests, and eval fixtures are updated. The shared GitHub mock lists installations for getsentry and acme, the owners that tests and evals write to. The junior Postgres test template now applies the GitHub plugin migrations, as junior upgrade does in a deployment.

Not in this PR: rejecting a GitHub trigger at save time when the App is not installed on its owner. That needs a small provider-neutral async check hook in core, and it should build on #2407. With the stored installations, the GitHub side is a database read. It goes in a follow-up PR. Until then, triggers for accounts without the App save but get no deliveries, which is the same as today.

Open question: confirm which account owns the prod App and that the App is installed on it. That installation is the default for reads that name no owner.

Deploy: junior upgrade applies the new migration. The table fills itself on the first token request. After release, remove GITHUB_INSTALLATION_ID from junior-prod.

Refs #2404

via David Cramer.

--

View Session [Sentry]

sentry-junior Bot and others added 3 commits October 30, 2026 02:46
Work in progress. Webhook tests in webhook-outcomes.test.ts are not yet
updated for the removed installation-ID route options.

Co-Authored-By: David Cramer <david@sentry.io>
…lations

Co-Authored-By: David Cramer <david@sentry.io>
Drop the unused skip log and update webhook tests for the removed
installation-ID route options.

Co-Authored-By: David Cramer <david@sentry.io>
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 30, 2026 4:50pm UTC

Request Review

@sentry-evals

sentry-evals Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Sentry Evals

junior-behavioral

Run Status Passed Failed Errored Cost Tokens Duration
Head Error 76 5 1 $0.77 0 531.53s

Base commit 8415974 has no eval run to compare against.

junior-guardian

Run Status Passed Failed Errored Cost Tokens Duration
Head Passed 94 0 0 $0.03 0 27.22s

Base commit 8415974 has no eval run to compare against.

junior-integration

Run Status Passed Failed Errored Cost Tokens Duration
Head Failed 99 4 0 $0.62 0 330.32s

Base commit 8415974 has no eval run to compare against.

sentry-junior Bot and others added 3 commits October 30, 2026 15:39
An App has at most one installation per account, so the owner of the
target repository or organization identifies the installation. Load the
App's installations once, cache them, and reload them on installation
webhooks. Lease scopes now name the owner, so one token serves every
repository of that account.

Co-Authored-By: David Cramer <david@sentry.io>
…lations

Co-Authored-By: David Cramer <david@sentry.io>
Co-Authored-By: David Cramer <david@sentry.io>
Keep a plugin-owned table of active installations by account login.
installation and installation_target webhooks save, rename, and remove
rows. A lookup that finds no row syncs the table from
GET /app/installations at most once a minute, which fills it on first
use and repairs missed webhooks. This replaces the cached installation
map.

Co-Authored-By: David Cramer <david@sentry.io>
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

1 screenshot change — 1 changed · 0 added · 0 removed

Review screenshots in Frameshift

Conversation Share Details · Desktop
Conversation Share Details · Desktop
Changed

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

…g sync

Remove a stored installation when GitHub rejects it as removed or suspended,
then sync and retry once. A sync now keeps rows that webhooks wrote while the
installation list request ran.

Split the issueCredential hook behavior into issue-credential.ts so
credential-support.ts stays under the file length limit.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ab6c225. Configure here.

Uninstall and suspend webhooks, rejected token mints, and syncs now clear the
account login instead of deleting the row. The row stays newer than an
in-flight sync list, so its insert conflicts and skips the installation.

This branch was successfully deployed

1 active deployment
Preview – junior-docs — c80a42a8 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants