Add connected service account "needs attention" and track GH App state.
Review Request #15197 — Created July 24, 2026 and updated
GitHub Apps can be uninstalled or suspended from the GitHub side, which
can cause our integration to start failing. This change makes us handle
that in a nice way. This involves several pieces:Connected Services "needs attention"
Accounts can now indicate that they need attention in some way, and
provide actions to handle this.This is now implemented for app-based accounts with GitHub in order to
allow the admin to reestablish the correction.Later work will involve using this for other hosting services (and
PAT-based GitHub connections) to have accounts indicate when their
credentials need to be updated.GitHub App webhook handler
The stub webhook handler has been fleshed out to verify the signature
header and handle several events:
deletedandsuspendmark the installation account with the state
so that they'll be flagged in the connected services list.createdcan heal a reinstall by refreshing the stored installation
ID, matched to the existing account. This allows a reinstall performed
entirely from the GitHub side to restore the connection without
requiring anything to happen inside Review Board.installation_repositorieskeeps therepository_selectionflag up
to date.installation_targetupdates the stored account name.
API request error handling
In the case of a missed webhook delivery, we can still end up with the
stored state being wrong (in either direction). API requests have been
updated to handle this in two ways:
-
Token minting will map a 403 or 404 response to the
suspended/uninstalled state (checking first to ensure that it's not
some other ephemeral failure or rate limit error). If we got the error
because the app had been uninstalled but is now reinstalled, we'll
adopt the new installation ID. -
403 or 404 errors for cached installation tokens will drop the token
and try again (limited to once per minute). -
If we have a stored non-active status, we'll recheck that state (also
limited to once per minute) before refusing to mint a new token.
GitHub reconnect
The reconnect action for suspended/uninstalled accounts will first try
to verify that state (in case of a missed webhook delivery), and if it's
still an issue, will deep link the user to the relevant page where they
can fix it.
- Ran unit tests.
- Suspended and deleted app installs and saw that the state was properly
reflected in the connected services list. - Reconnected suspended and deleted app installs and saw that state
healed correctly.
| Summary | ID |
|---|---|
| ummslvyqxyoqyzlozxkmksvtrxvnrpwn |
| Description | From | Last Updated |
|---|---|---|
|
line too long (80 > 79 characters) Column: 80 Error code: E501 |
|
|
|
line too long (83 > 79 characters) Column: 80 Error code: E501 |
|
|
|
Can probably get rid of this. |
|
|
|
Would it be better to change the return type to Literal['active', 'suspended', 'removed', '']? |
|
|
|
Would it be better to change the return type to Literal['active', 'suspended', 'removed', '']? |
|
|
|
Can we use _get_github_urls()['app_base'] here instead? |
|
|
|
We may want to do local_site=request.local_site here. |
|
|
|
Let's pull out account.login into a variable since we use it a lot below. |
|
|
|
redefinition of unused 'GitHubAppInstallationData' from line 36 Column: 5 Error code: F811 |
|
|
|
We generally keep TypedDict fields in alphabetical order (in required/not-required groups). |
|
|
|
Small nit, but can you move the example to a new paragraph so it stands out better from the description? |
|
|
|
These are missing "Version Added". Also to check, what's the benefit to using strings here over an enum? |
|
|
|
Can we alphabetize these? |
|
|
|
Can this be InstallationStatus? |
|
|
|
No parens here. |
|
|
|
AI smell. We never use this. |
|
|
|
This should be pulled out into a variable. |
|
|
|
Blank line required between these. |
|
|
|
We should stick with the standard grouping of required, optional. And alphabetical within that. Same as a TypedDict. |
|
|
|
Summary must be one line. |
|
|
|
These should be in alphabetical order. |
|
|
|
Missing parens. |
|
|
|
Missing parens, and should be in alphabetical order. |
|
|
|
The )) for each string should be on its own line, so the string is self-contained. |
|
|
|
No parens needed here. |
|
|
|
Looks like we can just do a simple if and return [reconnect_item] or []. |
|
|
|
Can this be a Sequence? |
|
|
|
This would fit on one line. |
|
|
|
Can you update this for alphabetical order? |
|
|
|
Should we check explicitly for the removed state? If we add a new state for any reason in the future, … |
|
|
|
To be safe, we should escape arguments going into the URL. |
|
|
|
To be safe, we should escape values going into URLs. |
|
|
|
If this can't be imported top-level, can we import at the top of the function like we typically do? |
|
|
|
Can you pull this out into a variable? |
|
|
|
These are missing docs. |
|
|
|
Can you pull this out into a variable? Helps with readability/maintainability. |
|
|
|
SyntaxError: invalid decimal literal Column: 20 Error code: E999 |
|
|
|
SyntaxError: invalid decimal literal Column: 20 Error code: E999 |
|
- Commits:
-
Summary ID ummslvyqxyoqyzlozxkmksvtrxvnrpwn ummslvyqxyoqyzlozxkmksvtrxvnrpwn - Diff:
-
Revision 2 (+5448 -340)
Checks run (2 succeeded)
- Commits:
-
Summary ID ummslvyqxyoqyzlozxkmksvtrxvnrpwn ummslvyqxyoqyzlozxkmksvtrxvnrpwn - Diff:
-
Revision 3 (+5546 -412)
- Commits:
-
Summary ID ummslvyqxyoqyzlozxkmksvtrxvnrpwn ummslvyqxyoqyzlozxkmksvtrxvnrpwn - Diff:
-
Revision 4 (+5544 -412)
Checks run (2 succeeded)
-
-
-
Small nit, but can you move the example to a new paragraph so it stands out better from the description?
-
These are missing "Version Added".
Also to check, what's the benefit to using strings here over an enum?
-
-
-
-
-
-
-
We should stick with the standard grouping of required, optional. And alphabetical within that. Same as a
TypedDict. -
-
-
-
-
-
-
-
-
-
-
Should we check explicitly for the removed state? If we add a new state for any reason in the future, this may produce the wrong result.
-
-
-
-
-
-
- Commits:
-
Summary ID ummslvyqxyoqyzlozxkmksvtrxvnrpwn ummslvyqxyoqyzlozxkmksvtrxvnrpwn - Diff:
-
Revision 5 (+5640 -420)
- Commits:
-
Summary ID ummslvyqxyoqyzlozxkmksvtrxvnrpwn ummslvyqxyoqyzlozxkmksvtrxvnrpwn - Diff:
-
Revision 6 (+5624 -428)