src-auth-perms-sync 0.5.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.github/workflows/validate.yml +7 -2
  2. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.gitignore +2 -5
  3. src_auth_perms_sync-0.7.0/.markdownlint-cli2.yaml +15 -0
  4. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/AGENTS.md +12 -3
  5. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/PKG-INFO +54 -6
  6. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/README.md +53 -5
  7. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/TODO.md +1 -11
  8. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/engineering-requests.md +9 -9
  9. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/memory-analysis/mapping-efficiency.md +2 -2
  10. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/memory-analysis/memory-efficiency.md +3 -3
  11. src_auth_perms_sync-0.7.0/examples/import.py +63 -0
  12. src_auth_perms_sync-0.7.0/package-lock.json +1377 -0
  13. src_auth_perms_sync-0.7.0/package.json +11 -0
  14. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/pyproject.toml +4 -1
  15. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/__init__.py +2 -0
  16. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/cli.py +152 -34
  17. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/orgs/command.py +2 -2
  18. src_auth_perms_sync-0.7.0/src/src_auth_perms_sync/orgs/queries.py +90 -0
  19. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/orgs/sync.py +502 -97
  20. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/orgs/types.py +6 -0
  21. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/command.py +106 -30
  22. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/full_set.py +3 -2
  23. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/mapping.py +1 -1
  24. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/maps.py +22 -0
  25. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/queries.py +39 -5
  26. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/sourcegraph.py +6 -0
  27. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/workflow.py +16 -0
  28. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/queries.py +10 -1
  29. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/run_context.py +8 -0
  30. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/saml_groups.py +92 -4
  31. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/sourcegraph.py +4 -0
  32. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/types.py +24 -0
  33. src_auth_perms_sync-0.7.0/tests/confusables.py +215 -0
  34. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/case_runner.py +206 -6
  35. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/set-users-sync-saml-orgs-noop/before.json +108 -0
  36. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/set-users-sync-saml-orgs-noop/maps.yaml +8 -0
  37. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/set-users-sync-saml-orgs-scoped/after.json +109 -0
  38. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/set-users-sync-saml-orgs-scoped/before.json +104 -0
  39. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/set-users-sync-saml-orgs-scoped/maps.yaml +8 -0
  40. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/sync-saml-orgs-full-cleanup/after.json +111 -0
  41. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/sync-saml-orgs-full-cleanup/before.json +106 -0
  42. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/sync-saml-orgs-users-scoped/after.json +113 -0
  43. src_auth_perms_sync-0.7.0/tests/e2e/fixtures/sync-saml-orgs-users-scoped/before.json +107 -0
  44. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/test_local_cases.py +84 -0
  45. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/run.py +5 -1
  46. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/tests.yaml +916 -27
  47. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_cli_config.py +63 -13
  48. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_command_files.py +1 -1
  49. src_auth_perms_sync-0.7.0/tests/unit/test_confusables.py +35 -0
  50. src_auth_perms_sync-0.7.0/tests/unit/test_mapping_rules.py +116 -0
  51. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_module_api.py +57 -0
  52. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_saml_groups.py +86 -0
  53. src_auth_perms_sync-0.5.0/.env.example +0 -36
  54. src_auth_perms_sync-0.5.0/.markdownlint-cli2.yaml +0 -9
  55. src_auth_perms_sync-0.5.0/dev/PLAN.md +0 -458
  56. src_auth_perms_sync-0.5.0/src/src_auth_perms_sync/orgs/queries.py +0 -44
  57. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.github/CODEOWNERS +0 -0
  58. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.github/workflows/ci.yml +0 -0
  59. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.github/workflows/release.yml +0 -0
  60. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/.python-version +0 -0
  61. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/LICENSE +0 -0
  62. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/SECURITY.md +0 -0
  63. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/audit-dead-code.md +0 -0
  64. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/hooks/pre-commit +0 -0
  65. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/memory-analysis/memory-efficiency-analyze.py +0 -0
  66. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/memory-analysis/memory-efficiency-generate.py +0 -0
  67. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/memory-analysis/memory-efficiency-monitor-sourcegraph.sh +0 -0
  68. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/dev/update-python-versions.md +0 -0
  69. /src_auth_perms_sync-0.5.0/maps-example.yaml → /src_auth_perms_sync-0.7.0/examples/maps.yaml +0 -0
  70. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/renovate.json +0 -0
  71. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/__main__.py +0 -0
  72. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/orgs/__init__.py +0 -0
  73. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/__init__.py +0 -0
  74. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/apply.py +0 -0
  75. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/restore.py +0 -0
  76. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/snapshot.py +0 -0
  77. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/permissions/types.py +0 -0
  78. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/__init__.py +0 -0
  79. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/backups.py +0 -0
  80. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/src/src_auth_perms_sync/shared/site_config.py +0 -0
  81. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/README.md +0 -0
  82. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/__init__.py +0 -0
  83. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/__init__.py +0 -0
  84. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-by-email-and-list/after.json +0 -0
  85. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-by-email-and-list/before.json +0 -0
  86. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-by-email-and-list/maps.yaml +0 -0
  87. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-preserves-existing/after.json +0 -0
  88. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-preserves-existing/before.json +0 -0
  89. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/add-users-preserves-existing/maps.yaml +0 -0
  90. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/and-filters-intersect/after.json +0 -0
  91. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/and-filters-intersect/before.json +0 -0
  92. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/and-filters-intersect/maps.yaml +0 -0
  93. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/empty-maps-noop/before.json +0 -0
  94. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/empty-maps-noop/maps.yaml +0 -0
  95. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-dry-run/before.json +0 -0
  96. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-dry-run/maps.yaml +0 -0
  97. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-preserves-pending/after.json +0 -0
  98. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-preserves-pending/before.json +0 -0
  99. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-preserves-pending/maps.yaml +0 -0
  100. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-removes-stale-grant/after.json +0 -0
  101. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-removes-stale-grant/before.json +0 -0
  102. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-removes-stale-grant/maps.yaml +0 -0
  103. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-unions/after.json +0 -0
  104. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-unions/before.json +0 -0
  105. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-unions/maps.yaml +0 -0
  106. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-with-backup/after.json +0 -0
  107. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-with-backup/before.json +0 -0
  108. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/full-overwrite-with-backup/maps.yaml +0 -0
  109. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/get-full-snapshot/before.json +0 -0
  110. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/get-repos-without-explicit-perms/before.json +0 -0
  111. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/get-user-grants/before.json +0 -0
  112. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-bad-regex/before.json +0 -0
  113. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-bad-regex/maps.yaml +0 -0
  114. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-missing-repos-section/before.json +0 -0
  115. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-missing-repos-section/maps.yaml +0 -0
  116. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-restore-wrong-schema-version/before.json +0 -0
  117. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-restore-wrong-schema-version/snapshot.json +0 -0
  118. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-created-after-date/before.json +0 -0
  119. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-created-after-date/maps.yaml +0 -0
  120. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-repos-created-after-date/before.json +0 -0
  121. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-repos-created-after-date/maps.yaml +0 -0
  122. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-unknown-repo/before.json +0 -0
  123. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-unknown-repo/maps.yaml +0 -0
  124. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-unknown-user/before.json +0 -0
  125. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-set-unknown-user/maps.yaml +0 -0
  126. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-unknown-selector-field/before.json +0 -0
  127. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/invalid-unknown-selector-field/maps.yaml +0 -0
  128. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/match-provider-and-host-fields/after.json +0 -0
  129. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/match-provider-and-host-fields/before.json +0 -0
  130. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/match-provider-and-host-fields/maps.yaml +0 -0
  131. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/no-match-noop/before.json +0 -0
  132. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/no-match-noop/maps.yaml +0 -0
  133. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/regex-filters-scope/after.json +0 -0
  134. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/regex-filters-scope/before.json +0 -0
  135. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/regex-filters-scope/maps.yaml +0 -0
  136. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-applies-snapshot/after.json +0 -0
  137. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-applies-snapshot/before.json +0 -0
  138. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-applies-snapshot/snapshot.json +0 -0
  139. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-dry-run-noop/before.json +0 -0
  140. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-dry-run-noop/snapshot.json +0 -0
  141. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-missing-file/before.json +0 -0
  142. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-restores-pending/after.json +0 -0
  143. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-restores-pending/before.json +0 -0
  144. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/restore-restores-pending/snapshot.json +0 -0
  145. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-filter/after.json +0 -0
  146. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-filter/before.json +0 -0
  147. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-filter/maps.yaml +0 -0
  148. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-live/after.json +0 -0
  149. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-live/before.json +0 -0
  150. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/saml-group-live/maps.yaml +0 -0
  151. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-created-after-sync-saml-orgs-dry-run/maps.yaml +0 -0
  152. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-created-after-temp-user/after.json +0 -0
  153. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-created-after-temp-user/before.json +0 -0
  154. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-created-after-temp-user/maps.yaml +0 -0
  155. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-full-sync-saml-orgs-dry-run/maps.yaml +0 -0
  156. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after/after.json +0 -0
  157. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after/before.json +0 -0
  158. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after/maps.yaml +0 -0
  159. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after-noop/before.json +0 -0
  160. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after-noop/maps.yaml +0 -0
  161. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-created-after-sync-saml-orgs-dry-run/maps.yaml +0 -0
  162. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-filter/after.json +0 -0
  163. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-filter/before.json +0 -0
  164. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-filter/maps.yaml +0 -0
  165. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-sync-saml-orgs-dry-run/maps.yaml +0 -0
  166. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-without-explicit-perms/after.json +0 -0
  167. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-without-explicit-perms/before.json +0 -0
  168. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-without-explicit-perms/maps.yaml +0 -0
  169. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-repos-without-perms-sync-saml-orgs-dry-run/maps.yaml +0 -0
  170. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-created-after/after.json +0 -0
  171. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-created-after/before.json +0 -0
  172. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-created-after/maps.yaml +0 -0
  173. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-created-after-noop/before.json +0 -0
  174. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-created-after-noop/maps.yaml +0 -0
  175. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-sync-saml-orgs-dry-run/maps.yaml +0 -0
  176. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms/after.json +0 -0
  177. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms/before.json +0 -0
  178. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms/maps.yaml +0 -0
  179. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms-default-batch/after.json +0 -0
  180. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms-default-batch/before.json +0 -0
  181. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-explicit-perms-default-batch/maps.yaml +0 -0
  182. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/e2e/fixtures/set-users-without-perms-sync-saml-orgs-dry-run/maps.yaml +0 -0
  183. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/integration/__init__.py +0 -0
  184. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/integration/test_cli_entrypoint.py +0 -0
  185. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/setup.py +0 -0
  186. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/setup.yaml +0 -0
  187. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/__init__.py +0 -0
  188. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_apply.py +0 -0
  189. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_backups.py +0 -0
  190. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_command_additive.py +0 -0
  191. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_maps.py +0 -0
  192. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_permissions_sourcegraph.py +0 -0
  193. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_restore.py +0 -0
  194. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/tests/unit/test_snapshot.py +0 -0
  195. {src_auth_perms_sync-0.5.0 → src_auth_perms_sync-0.7.0}/uv.lock +0 -0
@@ -127,6 +127,7 @@ jobs:
127
127
  runs-on: ubuntu-24.04
128
128
  env:
129
129
  MARKDOWNLINT_CLI2_VERSION: "0.22.1"
130
+ MARKDOWNLINT_RELATIVE_LINKS_VERSION: "5.1.0"
130
131
 
131
132
  steps:
132
133
  - name: Check out code
@@ -139,10 +140,14 @@ jobs:
139
140
  uses: actions/cache@v5
140
141
  with:
141
142
  path: ~/.npm
142
- key: npm-${{ runner.os }}-markdownlint-cli2-${{ env.MARKDOWNLINT_CLI2_VERSION }}
143
+ key: npm-${{ runner.os }}-markdownlint-cli2-${{ env.MARKDOWNLINT_CLI2_VERSION }}-relative-links-${{ env.MARKDOWNLINT_RELATIVE_LINKS_VERSION }}
143
144
 
144
145
  - name: Lint Markdown
145
- run: npx --yes "markdownlint-cli2@${MARKDOWNLINT_CLI2_VERSION}"
146
+ run: >
147
+ npx --yes
148
+ -p "markdownlint-cli2@${MARKDOWNLINT_CLI2_VERSION}"
149
+ -p "markdownlint-rule-relative-links@${MARKDOWNLINT_RELATIVE_LINKS_VERSION}"
150
+ markdownlint-cli2
146
151
 
147
152
  python:
148
153
  name: Validate Python
@@ -7,13 +7,13 @@ __pycache__
7
7
  .pytest_cache
8
8
  .ruff_cache
9
9
  .venv
10
+ node_modules/
10
11
  *.env*
11
12
  *.gql
12
13
  *.py[cod]
13
14
  *.py[oc]
14
15
  *.swp
15
16
  *.tsv
16
- *.yaml
17
17
  build/
18
18
  dist/
19
19
  logs*/
@@ -22,7 +22,4 @@ src-auth-perms-sync-runs/
22
22
  wheels/
23
23
 
24
24
  # Allow
25
- !.env.example
26
- !.markdownlint-cli2.yaml
27
- !maps-example.yaml
28
- !tests/**/*.yaml
25
+ !examples/
@@ -0,0 +1,15 @@
1
+ globs:
2
+ - "**/*.md"
3
+ ignores:
4
+ - ".venv/**"
5
+ - "build/**"
6
+ - "dist/**"
7
+ - "node_modules/**"
8
+ modulePaths:
9
+ - "./node_modules"
10
+ customRules:
11
+ - markdownlint-rule-relative-links
12
+ config:
13
+ MD013:
14
+ line_length: 100
15
+ relative-links: true
@@ -12,7 +12,11 @@
12
12
  actionlint
13
13
 
14
14
  ### Markdown files
15
- npx --yes markdownlint-cli2@0.22.1
15
+ npx --yes -p markdownlint-cli2@0.22.1 -p markdownlint-rule-relative-links@5.1.0 markdownlint-cli2
16
+
17
+ ### Confusable Unicode characters in non-Python files
18
+ # (ruff RUF001-RUF003 covers Python strings/comments/docstrings)
19
+ uv run python tests/confusables.py
16
20
 
17
21
  ### Python files
18
22
 
@@ -37,7 +41,8 @@ own checks.
37
41
 
38
42
  ```bash
39
43
  # Fast, no network (also what the pre-commit hook runs):
40
- # lint, format, pyright, unit + fixture tests, CLI rejection matrix,
44
+ # lint, format, pyright, confusable-character scan, unit + fixture
45
+ # tests, CLI rejection matrix,
41
46
  # randomized permission invariants
42
47
  uv run tests/run.py
43
48
 
@@ -97,7 +102,7 @@ uv run ruff format --check src/src_auth_perms_sync/ tests/
97
102
  uv run pyright
98
103
  uv run python -m unittest discover -s tests
99
104
  uv run src-auth-perms-sync --help
100
- npx --yes markdownlint-cli2@0.22.1
105
+ npx --yes -p markdownlint-cli2@0.22.1 -p markdownlint-rule-relative-links@5.1.0 markdownlint-cli2
101
106
  uv build --wheel --sdist --out-dir /tmp/src-auth-perms-sync-release-check --no-create-gitignore
102
107
  rm -rf /tmp/src-auth-perms-sync-release-check
103
108
  ```
@@ -252,6 +257,10 @@ removed names like `ev`, `ex`, `exc`, `cfg`, `conn`, `resp`, `fn`,
252
257
 
253
258
  Short names that ARE acceptable (don't rewrite these on sight):
254
259
 
260
+ - **Established domain abbreviations**: `org` / `orgs` for Sourcegraph
261
+ organizations, matching the `sync-saml-orgs` command, the `orgs/`
262
+ package, and the `sync_saml_orgs` config field. Do not expand these
263
+ back to `organization(s)` in identifiers.
255
264
  - **TypedDict / dataclass fields that mirror the wire format**: `id`,
256
265
  `url`, `kind` on `ExternalService` etc. These match GraphQL/JSON
257
266
  keys and renaming would break the contract.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: src-auth-perms-sync
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: Set Sourcegraph permissions from authentication provider data
5
5
  Project-URL: Homepage, https://github.com/sourcegraph/src-auth-perms-sync
6
6
  Project-URL: Issues, https://github.com/sourcegraph/src-auth-perms-sync/issues
@@ -170,11 +170,30 @@ for provider in get_result.auth_providers:
170
170
  for code_host in get_result.code_hosts:
171
171
  ...
172
172
 
173
+ # Mapping rules can be passed in memory instead of a maps YAML file —
174
+ # same structure and validation as maps.yaml entries:
175
+ rules: list[src.MappingRule] = [
176
+ {
177
+ "name": "Map 1",
178
+ "users": {"usernameRegexes": [".*"]},
179
+ "repos": {"codeHostConnection": {"kind": "GITHUB"}},
180
+ },
181
+ ]
182
+ result = src.Set(config, mapping_rules=rules)
183
+ # When files are enabled, the rules actually used are written into the
184
+ # run directory for auditability. Snapshots still gate apply=True unless
185
+ # no_files=True and no_backup=True are both set explicitly.
186
+
173
187
  # Other command wrappers:
174
188
  # result = src.Restore(config)
175
189
  # result = src.SyncSamlOrgs(config)
176
190
  ```
177
191
 
192
+ The import API does not read environment variables or `.env` files on its
193
+ own — those apply to the CLI only. Pass every value explicitly to
194
+ `src.Config(...)` (read `os.environ` yourself if you want env-driven
195
+ configuration, as the example above does).
196
+
178
197
  Module mode never touches your `logging` handlers or the root logger — your
179
198
  application's logging config stays in charge. To see progress messages:
180
199
 
@@ -203,7 +222,7 @@ snapshots that make `--apply` reversible.
203
222
  - Environment variables (CLI), or src.Config args (Python import)
204
223
  - `SRC_ENDPOINT`
205
224
  - `SRC_ACCESS_TOKEN` from a user with site-admin perms
206
- - See [.env.example](./.env.example)
225
+ - See [.env.example](./examples/.env.example)
207
226
 
208
227
  - YAML maps file
209
228
  - By default: `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
@@ -217,7 +236,7 @@ snapshots that make `--apply` reversible.
217
236
  - Each mapping rule takes
218
237
  - A map of filters for users
219
238
  - A map of filters for repos
220
- - See [maps-example.yaml](./maps-example.yaml)
239
+ - See [maps.yaml](./examples/maps.yaml)
221
240
  - An empty maps.yaml file is created for you on the first `get` run
222
241
 
223
242
  ## Usage: Permission sync
@@ -237,7 +256,7 @@ snapshots that make `--apply` reversible.
237
256
 
238
257
  - Edit `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
239
258
  - Add mapping rules under the `maps:` top level key
240
- - See [maps-example.yaml](./maps-example.yaml)
259
+ - See [maps.yaml](./examples/maps.yaml)
241
260
 
242
261
  3. **Set: Dry run**
243
262
 
@@ -278,7 +297,7 @@ snapshots that make `--apply` reversible.
278
297
  1. **Get user and org metadata**
279
298
 
280
299
  ```bash
281
- src-auth-perms-sync sync-saml-orgs
300
+ src-auth-perms-sync sync-saml-orgs --full
282
301
  ```
283
302
 
284
303
  - Queries the Sourcegraph instance for auth providers, users, users' SAML groups, and orgs
@@ -287,12 +306,41 @@ snapshots that make `--apply` reversible.
287
306
  2. **Apply org sync**
288
307
 
289
308
  ```bash
290
- src-auth-perms-sync sync-saml-orgs --apply
309
+ src-auth-perms-sync sync-saml-orgs --full --apply
291
310
  ```
292
311
 
293
312
  - Creates the orgs if they don't exist, and sync the members from the SAML groups to the orgs
294
313
  - `--sync-saml-orgs` can also be added to a `set` run, to run both at the same time
295
314
 
315
+ 3. **Scoped org sync for selected users**
316
+
317
+ ```bash
318
+ src-auth-perms-sync sync-saml-orgs --users alice,bob
319
+ src-auth-perms-sync sync-saml-orgs --created-after 2026-06-01
320
+ src-auth-perms-sync sync-saml-orgs --users-without-explicit-perms
321
+ ```
322
+
323
+ - Same user filters as `get` and `set`; a mode flag is required — there
324
+ is no bare `sync-saml-orgs`
325
+
326
+ ### Org sync behavior
327
+
328
+ - Org names are `synced-<configID>-<group name>` (non-alphanumeric characters
329
+ become `-`). The `synced-` prefix marks tool ownership: the sync only ever
330
+ modifies orgs whose name carries it, so manually created orgs are never touched.
331
+ - The org sync mode is always explicit — no surprises:
332
+ - **Full** (`sync-saml-orgs --full`, or `set --full` / `--repos*`
333
+ `--sync-saml-orgs`): converges every synced org against all users. A synced
334
+ org whose SAML group disappeared has all members removed, but the org itself
335
+ is kept (its settings survive in case the group comes back).
336
+ - **Scoped** (user filters on `sync-saml-orgs`, or `set --users` /
337
+ `--users-without-explicit-perms` / `--created-after` with
338
+ `--sync-saml-orgs`): syncs org membership for exactly the selected users —
339
+ per-user additions AND removals, computed from each user's own SAML
340
+ assertion and org list. Other users' memberships never change, and no full
341
+ user scan or org member listing is needed, so API traffic stays
342
+ proportional to the selection.
343
+
296
344
  ## Options
297
345
 
298
346
  Run `src-auth-perms-sync --help` for options
@@ -147,11 +147,30 @@ for provider in get_result.auth_providers:
147
147
  for code_host in get_result.code_hosts:
148
148
  ...
149
149
 
150
+ # Mapping rules can be passed in memory instead of a maps YAML file —
151
+ # same structure and validation as maps.yaml entries:
152
+ rules: list[src.MappingRule] = [
153
+ {
154
+ "name": "Map 1",
155
+ "users": {"usernameRegexes": [".*"]},
156
+ "repos": {"codeHostConnection": {"kind": "GITHUB"}},
157
+ },
158
+ ]
159
+ result = src.Set(config, mapping_rules=rules)
160
+ # When files are enabled, the rules actually used are written into the
161
+ # run directory for auditability. Snapshots still gate apply=True unless
162
+ # no_files=True and no_backup=True are both set explicitly.
163
+
150
164
  # Other command wrappers:
151
165
  # result = src.Restore(config)
152
166
  # result = src.SyncSamlOrgs(config)
153
167
  ```
154
168
 
169
+ The import API does not read environment variables or `.env` files on its
170
+ own — those apply to the CLI only. Pass every value explicitly to
171
+ `src.Config(...)` (read `os.environ` yourself if you want env-driven
172
+ configuration, as the example above does).
173
+
155
174
  Module mode never touches your `logging` handlers or the root logger — your
156
175
  application's logging config stays in charge. To see progress messages:
157
176
 
@@ -180,7 +199,7 @@ snapshots that make `--apply` reversible.
180
199
  - Environment variables (CLI), or src.Config args (Python import)
181
200
  - `SRC_ENDPOINT`
182
201
  - `SRC_ACCESS_TOKEN` from a user with site-admin perms
183
- - See [.env.example](./.env.example)
202
+ - See [.env.example](./examples/.env.example)
184
203
 
185
204
  - YAML maps file
186
205
  - By default: `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
@@ -194,7 +213,7 @@ snapshots that make `--apply` reversible.
194
213
  - Each mapping rule takes
195
214
  - A map of filters for users
196
215
  - A map of filters for repos
197
- - See [maps-example.yaml](./maps-example.yaml)
216
+ - See [maps.yaml](./examples/maps.yaml)
198
217
  - An empty maps.yaml file is created for you on the first `get` run
199
218
 
200
219
  ## Usage: Permission sync
@@ -214,7 +233,7 @@ snapshots that make `--apply` reversible.
214
233
 
215
234
  - Edit `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
216
235
  - Add mapping rules under the `maps:` top level key
217
- - See [maps-example.yaml](./maps-example.yaml)
236
+ - See [maps.yaml](./examples/maps.yaml)
218
237
 
219
238
  3. **Set: Dry run**
220
239
 
@@ -255,7 +274,7 @@ snapshots that make `--apply` reversible.
255
274
  1. **Get user and org metadata**
256
275
 
257
276
  ```bash
258
- src-auth-perms-sync sync-saml-orgs
277
+ src-auth-perms-sync sync-saml-orgs --full
259
278
  ```
260
279
 
261
280
  - Queries the Sourcegraph instance for auth providers, users, users' SAML groups, and orgs
@@ -264,12 +283,41 @@ snapshots that make `--apply` reversible.
264
283
  2. **Apply org sync**
265
284
 
266
285
  ```bash
267
- src-auth-perms-sync sync-saml-orgs --apply
286
+ src-auth-perms-sync sync-saml-orgs --full --apply
268
287
  ```
269
288
 
270
289
  - Creates the orgs if they don't exist, and sync the members from the SAML groups to the orgs
271
290
  - `--sync-saml-orgs` can also be added to a `set` run, to run both at the same time
272
291
 
292
+ 3. **Scoped org sync for selected users**
293
+
294
+ ```bash
295
+ src-auth-perms-sync sync-saml-orgs --users alice,bob
296
+ src-auth-perms-sync sync-saml-orgs --created-after 2026-06-01
297
+ src-auth-perms-sync sync-saml-orgs --users-without-explicit-perms
298
+ ```
299
+
300
+ - Same user filters as `get` and `set`; a mode flag is required — there
301
+ is no bare `sync-saml-orgs`
302
+
303
+ ### Org sync behavior
304
+
305
+ - Org names are `synced-<configID>-<group name>` (non-alphanumeric characters
306
+ become `-`). The `synced-` prefix marks tool ownership: the sync only ever
307
+ modifies orgs whose name carries it, so manually created orgs are never touched.
308
+ - The org sync mode is always explicit — no surprises:
309
+ - **Full** (`sync-saml-orgs --full`, or `set --full` / `--repos*`
310
+ `--sync-saml-orgs`): converges every synced org against all users. A synced
311
+ org whose SAML group disappeared has all members removed, but the org itself
312
+ is kept (its settings survive in case the group comes back).
313
+ - **Scoped** (user filters on `sync-saml-orgs`, or `set --users` /
314
+ `--users-without-explicit-perms` / `--created-after` with
315
+ `--sync-saml-orgs`): syncs org membership for exactly the selected users —
316
+ per-user additions AND removals, computed from each user's own SAML
317
+ assertion and org list. Other users' memberships never change, and no full
318
+ user scan or org member listing is needed, so API traffic stays
319
+ proportional to the selection.
320
+
273
321
  ## Options
274
322
 
275
323
  Run `src-auth-perms-sync --help` for options
@@ -1,15 +1,5 @@
1
1
  # TODO
2
2
 
3
- ## Follow-up: in-memory mapping rules for Set (PLAN.md Track A Phase A4)
4
-
5
- The rest of [PLAN.md](./PLAN.md) is implemented (src-py-lib v0.3.0 +
6
- the consumer refactor-logging-and-files PR). The one deliberately
7
- deferred piece, marked optional in the plan: let module callers pass
8
- parsed mapping rules to `Set` instead of a maps file, so the full
9
- get → assemble → dry-run loop never touches disk. Snapshots must stay
10
- on disk for `--apply` (reversibility invariant); `no_files` + `apply`
11
- must keep requiring `no_backup`.
12
-
13
3
  ## High priority: Remote trigger on demand
14
4
 
15
5
  - Sourcegraph webhook for new user coming in v7.4.0
@@ -88,7 +78,7 @@ If/when we revisit:
88
78
 
89
79
  Phase 1 now avoids per-repo username sets for non-overlapping full-set maps.
90
80
  If memory remains too high after re-measuring, implement the Phase 2 grouped
91
- plan in [mapping-efficiency.md](./mapping-efficiency.md): combine map-entry
81
+ plan in [mapping-efficiency.md](./memory-analysis/mapping-efficiency.md): combine map-entry
92
82
  overlays into final groups of repos that share the same desired username tuple.
93
83
 
94
84
  ## Low priority: Expand group-membership filters beyond SAML
@@ -1,7 +1,7 @@
1
1
  # Engineering requests
2
2
 
3
3
  Use this when opening Sourcegraph Engineering issues from memory-efficiency
4
- evidence. Capture steps stay in [memory-efficiency.md](./memory-efficiency.md);
4
+ evidence. Capture steps stay in [memory-efficiency.md](./memory-analysis/memory-efficiency.md);
5
5
  this file keeps the request-ready problem statement, evidence, proposed API
6
6
  shape, and copy/paste issue text.
7
7
 
@@ -85,16 +85,16 @@ small `set` command. Instance: single `pgsql-0` on an 8-core node.
85
85
 
86
86
  Observed during the concurrent captures:
87
87
 
88
- - `pgsql-0` CPU (`kubectl top`): 7,636–7,683 millicores of 8,000 (saturated).
89
- - `frontend` / `gitserver` CPU: 124–138m / 2–3m (idle bystanders).
88
+ - `pgsql-0` CPU (`kubectl top`): 7,636-7,683 millicores of 8,000 (saturated).
89
+ - `frontend` / `gitserver` CPU: 124-138m / 2-3m (idle bystanders).
90
90
  - `pg_stat_activity`: 29 active statements, all
91
91
  `permsStore.ListUserPermissions`, **zero wait events** — pure CPU, no lock
92
92
  contention.
93
93
  - `pg_stat_statements`: `permsStore.ListUserPermissions` at 24,026 calls,
94
94
  27,635.6s total, 1,150ms mean.
95
- - Per-client capture throughput: 23 users/sec solo → 2–4 users/sec at 4-way
95
+ - Per-client capture throughput: 23 users/sec solo → 2-4 users/sec at 4-way
96
96
  concurrency.
97
- - Aggregate throughput: 8–16 users/sec at 4-way — **below the 23 users/sec a
97
+ - Aggregate throughput: 8-16 users/sec at 4-way — **below the 23 users/sec a
98
98
  single client achieves alone** (negative scaling).
99
99
  - ALB (CloudWatch): no 5xx, no rejected connections — the edge and frontend
100
100
  are not the bottleneck.
@@ -104,7 +104,7 @@ Observed during the concurrent captures:
104
104
  Implications for the engineering request:
105
105
 
106
106
  - A single per-user `permissionsInfo.repositories(source: API)` read costs
107
- roughly 0.3–0.4s of Postgres CPU at this state size (1,150ms mean execution
107
+ roughly 0.3-0.4s of Postgres CPU at this state size (1,150ms mean execution
108
108
  under contention), so one operator at modest parallelism can saturate the
109
109
  database by itself, and two concurrent operators degrade each other below
110
110
  single-operator throughput.
@@ -112,7 +112,7 @@ Implications for the engineering request:
112
112
  client read timeout, retries re-run the same expensive queries, adding load
113
113
  exactly when the database is saturated.
114
114
  - A bulk read API (one query returning explicit grants for many users or for
115
- whole repos) would replace ~10,000 × ~1s statements per capture with a
115
+ whole repos) would replace ~10,000 x ~1s statements per capture with a
116
116
  single scan, and would also make concurrent operators viable.
117
117
 
118
118
  ## Sourcegraph codepath findings
@@ -142,7 +142,7 @@ from `github.com/sourcegraph/sourcegraph`:
142
142
  ## Presence-check resolver internals (2026-06-12)
143
143
 
144
144
  Measured on the 10k-user / 50k-repo test instance, the presence probe
145
- `User.permissionsInfo.repositories(source: API, first: 1)` costs 225–350ms of
145
+ `User.permissionsInfo.repositories(source: API, first: 1)` costs 225-350ms of
146
146
  server work per user, and alias batching barely helps (21,004 single-user
147
147
  probes averaged 351.6ms; 25-user batches averaged 5,616ms ≈ 224.7ms/user). A
148
148
  single `set --users-without-explicit-perms` run probing all 10,002 users at
@@ -180,7 +180,7 @@ Client-side mitigation shipped in `src-auth-perms-sync` (2026-06-12):
180
180
  locally BEFORE probing, so probes scale with the rule-matched user count
181
181
  instead of the instance's user count, and user hydration runs as aliased
182
182
  25-user batches instead of one `UserByID` request per user. The remaining
183
- inherent cost — ~225ms × probed user — is exactly what the
183
+ inherent cost — ~225ms x probed user — is exactly what the
184
184
  presence/filter API requested below would remove, and
185
185
  `get --users-without-explicit-perms` still has to probe every active user
186
186
  because its semantics are instance-wide.
@@ -64,7 +64,7 @@ user-group-b -> repo-group-2
64
64
  Each `maps:` entry is naturally a grouped rule:
65
65
 
66
66
  ```text
67
- selected users × selected repos
67
+ selected users x selected repos
68
68
  ```
69
69
 
70
70
  The maps schema keeps this restrictive: `users:` and `repos:` are selector
@@ -91,7 +91,7 @@ The old full-set planner immediately expanded every map entry into:
91
91
  repo_id -> set(username)
92
92
  ```
93
93
 
94
- That is expensive for rectangular maps such as `10000 users × 1000 repos`:
94
+ That is expensive for rectangular maps such as `10000 users x 1000 repos`:
95
95
  the username strings are shared, but each repo owns a large Python set with one
96
96
  hash-table entry per planned grant.
97
97
 
@@ -4,7 +4,7 @@ Use this when full snapshot capture or full-set apply is slow. The goal is to
4
4
  correlate `src-auth-perms-sync` structured logs with Sourcegraph Jaeger spans
5
5
  and pod/Postgres load. Request-ready evidence and copy/paste text for
6
6
  Sourcegraph Engineering live in
7
- [engineering-requests.md](./engineering-requests.md).
7
+ [engineering-requests.md](../engineering-requests.md).
8
8
 
9
9
  ## Capture a focused trace
10
10
 
@@ -157,7 +157,7 @@ The lower-level helper remains available for focused profiling outside a full
157
157
  e2e run:
158
158
 
159
159
  ```bash
160
- dev/memory-efficiency-monitor-sourcegraph.sh \
160
+ dev/memory-analysis/memory-efficiency-monitor-sourcegraph.sh \
161
161
  --namespace m \
162
162
  --output-dir /tmp/src-auth-perms-sync-sourcegraph-load-$(date -u +%Y%m%d-%H%M%S)
163
163
  ```
@@ -174,4 +174,4 @@ run.
174
174
 
175
175
  Request-ready trace findings, stress evidence, Sourcegraph codepath notes,
176
176
  proposed GraphQL APIs, and copy/paste issue text now live in
177
- [engineering-requests.md](./engineering-requests.md).
177
+ [engineering-requests.md](../engineering-requests.md).
@@ -0,0 +1,63 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = ["src-auth-perms-sync"]
4
+ # ///
5
+ """Sync Sourcegraph repo permissions from your own Python code.
6
+
7
+ export SRC_ENDPOINT="https://sourcegraph.example.com"
8
+ export SRC_ACCESS_TOKEN="sgp_..." # site-admin token
9
+ uv run example-import.py
10
+ """
11
+
12
+ import logging
13
+ import os
14
+
15
+ import src_auth_perms_sync as src
16
+
17
+ # Configure your logger
18
+ logging.basicConfig(level=logging.INFO)
19
+
20
+ # The import API does not read environment variables or .env files on its
21
+ # own (the CLI does); pass every value explicitly.
22
+ config = src.Config(
23
+ src_endpoint=os.environ["SRC_ENDPOINT"],
24
+ src_access_token=os.environ["SRC_ACCESS_TOKEN"],
25
+ )
26
+
27
+ # Discover the instance's auth providers and code hosts
28
+ discovery = src.Get(config)
29
+ for auth_provider in discovery.auth_providers:
30
+ print("auth provider:", auth_provider.get("displayName"))
31
+ for code_host in discovery.code_hosts:
32
+ print("code host:", code_host.get("displayName"))
33
+
34
+ # Configure your mapping rules
35
+ mapping_rules: list[src.MappingRule] = [
36
+ {
37
+ "name": "LOB1-GROUP1 members get the LOB1-SA1 repos",
38
+ "users": {"authProvider": {"samlGroup": "LOB1-GROUP1"}},
39
+ "repos": {"codeHostConnection": {"username": "LOB1-SA1"}},
40
+ },
41
+ {
42
+ "name": "LOB2-GROUP2 members get the LOB2-SA2 repos",
43
+ "users": {"authProvider": {"samlGroup": "LOB2-GROUP2"}},
44
+ "repos": {"codeHostConnection": {"username": "LOB2-SA2"}},
45
+ },
46
+ ]
47
+
48
+ # Run the set dry run, also syncing SAML groups to Sourcegraph organizations
49
+ result = src.Set(
50
+ config.model_copy(update={"full": True, "apply": False, "sync_saml_orgs": True}),
51
+ mapping_rules=mapping_rules,
52
+ )
53
+ # Print if it succeeded
54
+ print("permission sync dry run:", "ok" if result else "failed")
55
+
56
+ # Run the set apply, also syncing SAML groups to Sourcegraph organizations
57
+ result = src.Set(
58
+ config.model_copy(update={"full": True, "apply": True, "sync_saml_orgs": True}),
59
+ mapping_rules=mapping_rules,
60
+ )
61
+
62
+ # Print if it succeeded
63
+ print("permission sync apply:", "ok" if result else "failed")