Repository navigation
Move repository bindings when a credential is disconnected - #2122
Merged
Merged
Conversation
ppXD
force-pushed
the
fix/keep-older-clients-and-plain-http-deployments-working
branch
from
October 18, 2026 11:21
2d598a0 to
0f96f74
Compare
Connecting a new credential and then disconnecting the old one stranded
every repository bound through the old one: the connect found nothing to
carry forward (the owner's other credential was already live), and the
repair sweep only follows a credential connected after the revocation.
The repository stayed Error on a revoked row for good, and the only way
back was a per-repository Re-link that never touched group hooks.
Disconnect now takes an optional MoveBindingsToCredentialId and moves
the credential's repositories and group hooks in the same unit of work.
Left out, the bindings follow the owner's only other live connection,
but only when the owner is the one disconnecting a credential that is
still live: never for an Admin cutting off a teammate's token, never on a
retry of a disconnect already made, never for a team token or when two
connections compete. Connect-then-disconnect thus ends where
disconnect-then-connect always did, and no one else's grant is chosen for
them. A refused target fails before the provider is asked to revoke
anything. Unlinking a provider identity ends its credential the same way
and follows the same rule. GET /credentials/{id}/usage previews which of
the two outcomes a Disconnect will be, and the confirm dialog quotes it.
RelinkCredentialBindingsCommand moves everything a dead credential left,
a departed member's retired token included, in one action. It needs
repos.manage and credentials.manage, takes only the caller's own
connection or a team token, and asks the target to read every repository
first, so one it cannot read refuses the whole move and names it. Only an
answer about the target counts (401, 403, 404, insufficient scope, a
refused OAuth grant); an outage or rate limit is a retryable refusal that
blames no one, and one relink verifies at most 100 repositories.
GetCredentialDependentsQuery and GetRelinkSuggestionsQuery say what is
waiting and where it could go; for a credential that is still connected
the suggestions are exactly the targets a Disconnect accepts.
The departed-token retirement and the repair of stranded bindings read
their backlog through one shared pass: in pages behind an id cursor, so
candidates that can never succeed no longer hold the whole batch and
starve the rest; bounded in attempts and candidates examined as well as
in results; and a candidate that fails several passes in a row is set
aside for an hour rather than attempted and warned about every tick.
The disconnected-repository error and CredentialDisconnectedException
name only the remedies a person can perform today.
ppXD
changed the base branch from
fix/keep-older-clients-and-plain-http-deployments-working
to
main
October 18, 2026 11:22
ppXD
force-pushed
the
fix/move-bindings-when-a-credential-is-disconnected
branch
from
October 18, 2026 11:22
18ae07b to
519bfee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
RevokeCredentialCommandtakes an optionalMoveBindingsToCredentialId. Left out, the bindings follow the owner's only other live connection, but only when the owner is the one disconnecting a credential that is still live. An Admin cutting off a teammate's token, a retry of a disconnect already made, a team token, and a choice between two or more connections all move nothing, as before. Connect-then-disconnect now ends where disconnect-then-connect always did. Unlinking a provider identity ends its credential under the same rule. A refused target fails before the provider is asked to revoke anything.GET /credentials/{id}/usagepreviews which outcome a Disconnect will have (willMoveRepositoryCount,willMoveToDisplayName;activeRepositoryCountis the number that would break), and the Disconnect confirm dialog quotes it. The old fields keep their meaning, and an API that predates the fields is read as "nothing moves".RelinkCredentialBindingsCommand(POST /credentials/{id}/relink-bindings) moves everything a dead credential left, a departed member's retired token included, in one action. It needsrepos.manageandcredentials.manage, accepts only the caller's own connection or a team token on the same provider instance, and asks the target to read every repository first. A repository the target cannot read (401/403/404, insufficient scope, a refused OAuth grant) refuses the whole move and names it. A provider that does not answer (outage, rate limit) is a retryable refusal that blames no one. One relink verifies at most 100 repositories.GetCredentialDependentsQuerylists the repositories (with projects), group hooks, act-as identities and workflow triggers that lean on a credential.GetRelinkSuggestionsQueryranks the caller's own live connections first, then team tokens. For a credential that is still connected it offers exactly the targets a Disconnect accepts (the same owner's other connections).CredentialDisconnectedExceptionname only the remedies a person can perform today.TeamStandingandIRequireCredentialManagementsemantics are unchanged.Test plan
credentials.managecheck), suggestions equal what a Disconnect accepts, dependents filters, probe failure classification, the 100-repository bound, and poison-pill fairness for both sweeps with a real Postgres trigger.tsc --noEmit -p tsconfig.app.json, vitest (273 files), build. The dialog wording tests are red against the old modal.credential-binding-repairanddeparted-credential-retirementwarnings; a candidate should log once, then go quiet for an hour.