Preserve and restore pending explicit permissions#29
Merged
Conversation
Sourcegraph's setRepositoryPermissionsForUsers replaces a repo's whole explicit list, including pending rows (bindIDs that never resolved to a user), so every overwrite silently deleted pending grants the CLI could never recreate. Make the script neither create nor lose them: - Snapshots (schema v6) capture pending_users: bindID -> pending repos, via the authorizedUserRepositories late-binding fallback - set --full resends each repo's current pending bindIDs in overwrite payloads (from the before-snapshot, or fetched live with --no-backup) - restore replays the target snapshot's pending grants and wipes pending grants the snapshot does not contain - Snapshot diffs report pending bindIDs added/removed/changed; post-apply validation checks pending state was preserved exactly - Fixture fake mirrors the real resolver (unknown bindIDs become per-repo pending rows); new fixture cases and a randomized invariant cover preservation and restore Amp-Thread-ID: https://ampcode.com/threads/T-019eba2a-7d12-71b8-a491-ab2e78476f0b Co-authored-by: Amp <amp@ampcode.com>
The live fixture runner now carries per-repo pendingBindIDs from fixture state through the whole cycle: seeded with the before-state (unknown bindIDs become pending rows server-side), independently read back via the authorizedUserRepositories late-binding fallback, asserted to survive the apply exactly as the fixture says, and restored away with the original state. full-overwrite-unions seeds one pending bindID on a mapped repo (must survive its overwrite) and one on an unmapped canary (must stay untouched). Seeded bindIDs use the perms-sync-test-pending- prefix: tests/setup.py classifies leftovers from interrupted runs as synthetic and deletes exactly those rows with --apply (verified by GraphQL read-back), while still reporting and never deleting pending bindIDs of unknown origin. The live hygiene check points synthetic leftovers at setup --apply. Amp-Thread-ID: https://ampcode.com/threads/T-019eba2a-7d12-71b8-a491-ab2e78476f0b Co-authored-by: Amp <amp@ampcode.com>
182baf4 to
5962afa
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.
Problem
setRepositoryPermissionsForUsersreplaces a repo's whole explicit list — real grants AND pending rows (bindIDs that never resolved to a user, i.e. grant-before-first-login). Every overwrite this tool sent silently deleted pending grants on the touched repos, and snapshots only recorded a flat bindID list, so a restore could never recreate them: silent, irreversible data loss.Server-side mechanics confirmed in sourcegraph/sourcegraph: unresolved bindIDs in the mutation become
pending_repo_permissionsrows in the same transaction (scoped toservice_type='sourcegraph', so code-host-synced pending permissions are unreachable and unaffected), andauthorizedUserRepositoriesfalls back to the pending store for bindIDs with no matching user — the only API exposing which repos a pending bindID has.Change
The script now neither creates nor loses pending explicit permissions:
pending_users: bindID → pending repos, via theauthorizedUserRepositorieslate-binding fallback. Capture re-checks the pending list and drops bindIDs that became real users mid-run; any lookup failure aborts the capture rather than writing a snapshot with missing grants.set --fullresends each repo's current pending bindIDs in overwrite payloads (from the before-snapshot, or fetched live with--no-backup).restorereplays the target snapshot's pending grants — including repos that had only pending grants — and wipes pending grants the snapshot does not contain.set --users, user-scoped restore) use per-user mutations that never touch pending rows — unchanged.Testing
full-overwrite-preserves-pendingandrestore-restores-pending, plus a new randomized invariant: "set --full neither creates nor loses pending grants".pendingBindIDsfrom fixture state, independently reads them back, asserts they survive the apply, and restores them away.full-overwrite-unionsseeds one pending bindID on a mapped repo (survives its overwrite) and one on an unmapped canary (stays untouched).tests/setup.pydeletes leftover synthetic pending bindIDs (perms-sync-test-pending-*prefix, from interrupted runs) with--apply, verified by read-back; pending bindIDs of unknown origin are still reported and never deleted.getcaptures it →set --full --applypreserves it → manual wipe →restore --applyrecreates it exactly ("VALIDATION OK: post-restore state matches the snapshot exactly").Caveats
restorerefuses before.json files written by older releases (strict version check, consistent with previous bumps).sourcegraphnamespace; this tool preserves those too (indistinguishable from explicit-API pending grants).