Allow replacing a GitHub App's private key.
Review Request #15200 — Created July 28, 2026 and submitted
GitHub lets an administrator regenerate an app's private key and revoke
the old one. When that happens the key Review Board has stored can no
longer sign app JWTs, which breaks every installation of the app. There
was no way to recover short of recreating the whole app, since the
app-record account is hidden and has no credential-editing UI.This adds a "Rotate private key" action to the GitHub App connection. It
accepts a freshly-generated PEM key, validates that it is a usable RSA
private key, and stores it on the app-record account, restoring the
connection in place.
- Ran unit tests.
- Generated a new private key and uploaded it successfully.
| Summary | ID |
|---|---|
| nqztrxowytwkkklmvmyunyuwuxvlsyvp |
| Description | From | Last Updated |
|---|---|---|
|
continuation line over-indented for visual indent Column: 46 Error code: E127 |
|
|
|
continuation line over-indented for visual indent Column: 46 Error code: E127 |
|
|
|
I think we like to put all strings on their own lines now so how about: raise ValueError( 'The private … |
|
|
|
Same here, we can move the ) to its own line. |
|
|
|
To be on the safe side, we should urlquote these. |
|
|
|
The encode/decode is just a bit hard to read. Can we do: return encrypt_password( base64.b64encode(...) .decode('ascii') ) To check, though, … |
|
|
|
Should this be Final[int]? |
|
|
|
This is missing the full module path. |
|
|
|
Can we use a common function for this? That'll also make it easier for us to move away from encrypt_password … |
|
|
|
This is missing Version Added. |
|
|
|
Private functions go last. This is a major Claude smell. We never do this and mine keeps trying this. |
|
|
|
This is missing a translation. |
|
- Commits:
-
Summary ID nqztrxowytwkkklmvmyunyuwuxvlsyvp nqztrxowytwkkklmvmyunyuwuxvlsyvp - Diff:
-
Revision 2 (+1458 -94)
- Commits:
-
Summary ID nqztrxowytwkkklmvmyunyuwuxvlsyvp nqztrxowytwkkklmvmyunyuwuxvlsyvp - Diff:
-
Revision 3 (+1458 -94)
Checks run (2 succeeded)
- Commits:
-
Summary ID nqztrxowytwkkklmvmyunyuwuxvlsyvp nqztrxowytwkkklmvmyunyuwuxvlsyvp - Diff:
-
Revision 4 (+1458 -94)
Checks run (2 succeeded)
-
-
-
The encode/decode is just a bit hard to read. Can we do:
return encrypt_password( base64.b64encode(...) .decode('ascii') )To check, though, should we be base64-encoding here? A PEM is largely base64-encoded content already. If it were a DER, it'd be more reasonable to encode it.
-
-
-
Can we use a common function for this?
That'll also make it easier for us to move away from
encrypt_passwordhere as we move to cryptozoology. -
-
Private functions go last.
This is a major Claude smell. We never do this and mine keeps trying this.
-
- Commits:
-
Summary ID nqztrxowytwkkklmvmyunyuwuxvlsyvp nqztrxowytwkkklmvmyunyuwuxvlsyvp - Diff:
-
Revision 5 (+1566 -170)