Add GitHub App connectivity.
Review Request #15144 — Created June 30, 2026 and updated
This change adds a GitHub App as an alternative to Personal Access
Tokens. This involves a bunch of moving parts:
The actual GitHub App backend, which includes a redirect view that
will trigger the GitHub App Manifest flow, and callback views for
when the app has been created and when the app has been installed
into a user or organization. This ends up creating a hidden
HostingServiceAccountwhich holds the app registration keys, and
then each time the app gets installed (for a user or organization)
will create a newHostingServiceAccountfor that entry.Modifications to the GitHub client to use the app for authentication.
This involves minting short-lived RS256 JWT tokens for the actual API
requests to GitHub.Customized UI parts for the connected services list. This involves:
-
Special templates and flows for the "Connect a service" wizard which
lets users choose how they want to connect, and triggers for the new
app creation flow. -
An override for the entry template which adds an extra controls
panel that either encourages users to create the app (if they have
existing connections using PATs only), or provides a link into
GitHub's settings UI (allowing changes to URLs or transferring the
app registration to another owner).
This also currently includes a stub webhook handler (since GitHub Apps
have a single webhook endpoint for all authorized repositories), as well
as some commented-out bits for future enhancement.
- Connected a GitHub App and installed it into multiple organizations.
- Tested all aspects of the wizard flow, including creation of the app
and reassignment of any PAT-based repositories after app install. - Verified that API access works correctly when authenticating using the
app tokens. - Ran unit tests.
| Summary | ID |
|---|---|
| qmxulzkloxrsnquyqwrxwlrrquzplqpr |
| Description | From | Last Updated |
|---|---|---|
|
Can we make a pydantic model for the GitHub app data (the github_app data stored on the installation account)? |
|
|
|
Ok so thinking about local sites again (this is on my mind from neglecting to really consider local sites when … |
|
|
|
Do we nicely handle things if there's a lot of repositories here? Like having a vertical scrollbar if there's a … |
|
|
|
continuation line under-indented for visual indent Column: 34 Error code: E128 |
|
|
|
'reviewboard.site.models.LocalSite' imported but unused Column: 5 Error code: F401 |
|
|
|
Can we rename to AppManifestResponse. |
|
|
|
Can we rename to InstallationAccountResponse. |
|
|
|
Can you add a Version Added to this. |
|
|
|
In this change the App views were put into reviewboard.hostingsvcs.github.views and I don't see any other changes where you split … |
|
|
|
This needs a Version Added. |
|
|
|
Should we pass local_site=request.get('local_site') here? |
|
|
|
I think there will always be a request.local_site attribute because of our LocalSiteMiddleware. |
|
|
|
Same note about request.local_site always existing. |
|
|
|
line too long (87 > 79 characters) Column: 80 Error code: E501 |
|
|
|
line too long (85 > 79 characters) Column: 80 Error code: E501 |
|
|
|
Missing a docstring. |
|
|
|
Could this be Literal['user', 'organization', '']? |
|
|
|
Same comment as above. |
|
|
|
Seems like this could be Literal['all', 'selected']. |
|
|
|
Can you add a docstring for this. |
|
|
|
This can be removed, we do this below in the if condition. |
|
|
|
We do these isinstance checks on the app data a lot more than we do is_installation_account() or is_app_record_account(). Might be … |
|
|
|
This can be an elif or add back in the blank line before this. |
|
|
|
We can remove local_site here because we do a local_site = app_account.local_site in the view instead of using this value … |
|
|
|
Same comment as above. |
|
|
|
I'm getting a bit mixed up with the namings. We have "app-record accounts" and "installation accounts". A hosting service account … |
|
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 2 (+7694 -50)
Checks run (2 succeeded)
- Change Summary:
-
- Match accounts based on numeric ID, which stays constant across account renames.
- Make
is_authorized()returnTruefor GitHub AppHostingServiceAccounts.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 3 (+7994 -50)
Checks run (2 succeeded)
- Change Summary:
-
- Rebase on top of /r/15169/
- Redirect correctly back to connect wizard after app install.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 4 (+8108 -44)
Checks run (2 succeeded)
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 5 (+8106 -44)
Checks run (2 succeeded)
- Change Summary:
-
Update for renamed and changed APIs
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 6 (+8094 -44)
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 7 (+8092 -44)
Checks run (2 succeeded)
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 8 (+8088 -44)
Checks run (2 succeeded)
- Change Summary:
-
Do better account lookups for reassigning repos.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 9 (+8250 -44)
Checks run (2 succeeded)
-
-
Can we make a pydantic model for the GitHub app data (the
github_appdata stored on the installation account)? -
Do we nicely handle things if there's a lot of repositories here? Like having a vertical scrollbar if there's a bunch of repositories listed.
-
-
-
-
In this change the App views were put into
reviewboard.hostingsvcs.github.viewsand I don't see any other changes where you split them out into anapp_viewsmodule. -
-
-
-
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 10 (+8278 -44)
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 11 (+8280 -44)
Checks run (2 succeeded)
- Change Summary:
-
Make use of the newly moved
TestCase.login_userinstead of reinventing that wheel. - Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 12 (+8272 -44)
Checks run (2 succeeded)
- Change Summary:
-
Use Pydantic for the app/install records. I think the change was worth it but golly gee.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 13 (+8974 -68)
Checks run (2 succeeded)
-
-
Ok so thinking about local sites again (this is on my mind from neglecting to really consider local sites when implementing document review and then having to go in and add it later), is the idea for GitHub app-record accounts to be local-site dependent? This seems to be the current design because we pass
local_sitefor both the app-record accounts and installation accounts (e.g. inmake_connect_ui_context()we doHostingServiceAccount.objects.filter(service_name=..., local_site=request.local_site).But I know you mentioned possibly having a "GitHub App for all of RBCommons" in a reply to one of my other reviews. And we have
staff_member_requireddecorators on the views, which means only server admins (not local site team admins) will be able to access the views.So even though RBCommons will require its own development effort, I think we need to make some design decisions at this stage since we are currently passing around local sites in this code.
-
-
-
-
-
-
-
We do these
isinstancechecks on the app data a lot more than we dois_installation_account()oris_app_record_account(). Might be worth making someis_installation_data()is_app_record_data()functions? -
We can remove
local_sitehere because we do alocal_site = app_account.local_sitein the view instead of using this value that was passed in. -
-
I'm getting a bit mixed up with the namings. We have "app-record accounts" and "installation accounts". A hosting service account can be either of those.
Can we rename
_create_app_record()to_create_app_record_account()to clarify that. And then rename_create_app_account()to_create_app_installation_account()to specify that its an installation account. Because "app account" could really mean either a record account or installation account, you even name the record account variableapp_account.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 14 (+9080 -68)
Checks run (2 succeeded)
- Change Summary:
-
Reduce some duplication across test cases.
- Commits:
-
Summary ID qmxulzkloxrsnquyqwrxwlrrquzplqpr qmxulzkloxrsnquyqwrxwlrrquzplqpr - Diff:
-
Revision 15 (+9096 -68)
