src-auth-perms-sync 0.2.2__tar.gz → 0.3.1__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 (81) hide show
  1. src_auth_perms_sync-0.3.1/.github/CODEOWNERS +1 -0
  2. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.github/workflows/release.yml +79 -33
  3. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.github/workflows/validate.yml +4 -2
  4. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.gitignore +2 -0
  5. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/AGENTS.md +24 -80
  6. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/PKG-INFO +78 -51
  7. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/README.md +76 -49
  8. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/dev/TODO.md +30 -15
  9. src_auth_perms_sync-0.3.1/dev/mapping-efficiency.md +175 -0
  10. src_auth_perms_sync-0.3.1/dev/memory-efficiency-analyze.py +618 -0
  11. src_auth_perms_sync-0.3.1/dev/memory-efficiency-generate.py +1142 -0
  12. src_auth_perms_sync-0.3.1/dev/memory-efficiency-monitor-sourcegraph.sh +348 -0
  13. src_auth_perms_sync-0.3.1/dev/memory-efficiency.md +380 -0
  14. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/dev/test-end-to-end.py +877 -163
  15. src_auth_perms_sync-0.3.1/maps-example.yaml +130 -0
  16. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/pyproject.toml +8 -5
  17. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/renovate.json +12 -0
  18. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/__init__.py +11 -0
  19. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/cli.py +923 -0
  20. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/sync.py +140 -120
  21. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/apply.py +152 -135
  22. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/permissions/command.py +1609 -0
  23. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/full_set.py +230 -35
  24. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/permissions/mapping.py +865 -0
  25. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/maps.py +24 -28
  26. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/queries.py +116 -22
  27. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/restore.py +24 -15
  28. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/snapshot.py +275 -178
  29. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/permissions/sourcegraph.py +897 -0
  30. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/types.py +26 -12
  31. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/workflow.py +131 -26
  32. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/backups.py +0 -7
  33. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/queries.py +39 -18
  34. src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/shared/run_context.py +265 -0
  35. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/saml_groups.py +2 -2
  36. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/site_config.py +11 -8
  37. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/sourcegraph.py +17 -3
  38. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/types.py +7 -0
  39. src_auth_perms_sync-0.3.1/tests/integration/test_cli_entrypoint.py +76 -0
  40. src_auth_perms_sync-0.3.1/tests/unit/test_apply.py +102 -0
  41. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/unit/test_backups.py +2 -14
  42. src_auth_perms_sync-0.3.1/tests/unit/test_cli_config.py +734 -0
  43. src_auth_perms_sync-0.3.1/tests/unit/test_command_additive.py +280 -0
  44. src_auth_perms_sync-0.3.1/tests/unit/test_maps.py +612 -0
  45. src_auth_perms_sync-0.3.1/tests/unit/test_permissions_sourcegraph.py +272 -0
  46. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/unit/test_restore.py +4 -8
  47. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/unit/test_snapshot.py +97 -40
  48. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/uv.lock +257 -30
  49. src_auth_perms_sync-0.2.2/dev/git-worktrees.md +0 -89
  50. src_auth_perms_sync-0.2.2/dev/sourcegraph-explicit-permissions-tracing.md +0 -289
  51. src_auth_perms_sync-0.2.2/dev/test-plan.md +0 -207
  52. src_auth_perms_sync-0.2.2/maps-example.yaml +0 -39
  53. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/__init__.py +0 -1
  54. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/cli.py +0 -606
  55. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/permissions/command.py +0 -918
  56. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/permissions/mapping.py +0 -628
  57. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/permissions/sourcegraph.py +0 -391
  58. src_auth_perms_sync-0.2.2/src/src_auth_perms_sync/shared/run_context.py +0 -34
  59. src_auth_perms_sync-0.2.2/tests/integration/test_cli_entrypoint.py +0 -20
  60. src_auth_perms_sync-0.2.2/tests/unit/test_cli_config.py +0 -296
  61. src_auth_perms_sync-0.2.2/tests/unit/test_maps.py +0 -75
  62. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.env.example +0 -0
  63. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.github/workflows/ci.yml +0 -0
  64. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.markdownlint-cli2.yaml +0 -0
  65. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/.python-version +0 -0
  66. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/LICENSE +0 -0
  67. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/SECURITY.md +0 -0
  68. /src_auth_perms_sync-0.2.2/dev/dead-code-audit.md → /src_auth_perms_sync-0.3.1/dev/audit-dead-code.md +0 -0
  69. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/dev/hooks/pre-commit +0 -0
  70. /src_auth_perms_sync-0.2.2/dev/python-versions.md → /src_auth_perms_sync-0.3.1/dev/update-python-versions.md +0 -0
  71. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/__main__.py +0 -0
  72. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/__init__.py +0 -0
  73. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/command.py +0 -0
  74. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/queries.py +0 -0
  75. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/types.py +0 -0
  76. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/__init__.py +0 -0
  77. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/__init__.py +0 -0
  78. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/__init__.py +0 -0
  79. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/integration/__init__.py +0 -0
  80. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/unit/__init__.py +0 -0
  81. {src_auth_perms_sync-0.2.2 → src_auth_perms_sync-0.3.1}/tests/unit/test_saml_groups.py +0 -0
@@ -0,0 +1 @@
1
+ * @marcleblanc2
@@ -6,17 +6,17 @@ on:
6
6
  - "v*"
7
7
  workflow_dispatch:
8
8
  inputs:
9
- tag:
10
- description: "Existing release tag to publish, for example v0.1.0"
9
+ version:
10
+ description: "Package version to publish, for example 0.1.0 or v0.1.0"
11
11
  required: true
12
12
  type: string
13
13
 
14
14
  permissions:
15
- contents: write
15
+ contents: read
16
16
  pull-requests: read
17
17
 
18
18
  concurrency:
19
- group: release-${{ github.event.inputs.tag || github.ref_name }}
19
+ group: release-${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }}
20
20
  cancel-in-progress: false
21
21
 
22
22
  defaults:
@@ -24,15 +24,41 @@ defaults:
24
24
  shell: bash
25
25
 
26
26
  jobs:
27
+ release_ref:
28
+ name: Resolve release tag
29
+ runs-on: ubuntu-24.04
30
+ outputs:
31
+ tag: ${{ steps.release.outputs.tag }}
32
+ version: ${{ steps.release.outputs.version }}
33
+
34
+ steps:
35
+ - name: Resolve release tag
36
+ id: release
37
+ env:
38
+ RELEASE_INPUT: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }}
39
+ run: |
40
+ release_input="${RELEASE_INPUT}"
41
+ if [[ ! "${release_input}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
42
+ echo "::error title=Invalid release version::Use MAJOR.MINOR.PATCH or vMAJOR.MINOR.PATCH, got '${release_input}'."
43
+ exit 1
44
+ fi
45
+
46
+ release_version="${release_input#v}"
47
+ release_tag="v${release_version}"
48
+ echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}"
49
+ echo "version=${release_version}" >> "${GITHUB_OUTPUT}"
50
+
27
51
  validate:
28
52
  name: Validate
53
+ needs: release_ref
29
54
  uses: ./.github/workflows/validate.yml
30
55
  with:
31
- ref: ${{ github.event.inputs.tag || github.ref }}
56
+ ref: ${{ needs.release_ref.outputs.tag }}
32
57
  build-package: false
33
58
 
34
59
  wheelhouse:
35
60
  name: ${{ matrix.platform }}-py311 wheelhouse
61
+ needs: release_ref
36
62
  runs-on: ${{ matrix.runs_on }}
37
63
  strategy:
38
64
  fail-fast: false
@@ -61,7 +87,7 @@ jobs:
61
87
  with:
62
88
  fetch-depth: 0
63
89
  persist-credentials: false
64
- ref: ${{ github.event.inputs.tag || github.ref }}
90
+ ref: ${{ needs.release_ref.outputs.tag }}
65
91
 
66
92
  - name: Set up Python
67
93
  uses: actions/setup-python@v6
@@ -81,8 +107,10 @@ jobs:
81
107
 
82
108
  - name: Validate release inputs
83
109
  id: release
110
+ env:
111
+ RELEASE_TAG: ${{ needs.release_ref.outputs.tag }}
84
112
  run: |
85
- release_tag="${{ github.event.inputs.tag || github.ref_name }}"
113
+ release_tag="${RELEASE_TAG}"
86
114
  if [[ ! "${release_tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
87
115
  echo "::error title=Invalid release tag::Use a vMAJOR.MINOR.PATCH tag, got '${release_tag}'."
88
116
  exit 1
@@ -100,19 +128,8 @@ jobs:
100
128
  exit 1
101
129
  fi
102
130
 
103
- project_version=$(uv run --frozen python - <<'PY'
104
- import tomllib
105
-
106
- with open("pyproject.toml", "rb") as pyproject_file:
107
- print(tomllib.load(pyproject_file)["project"]["version"])
108
- PY
109
- )
110
- if [[ "v${project_version}" != "${release_tag}" ]]; then
111
- echo "::error title=Version mismatch::pyproject.toml version '${project_version}' does not match tag '${release_tag}'."
112
- exit 1
113
- fi
114
-
115
131
  echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}"
132
+ echo "version=${release_tag#v}" >> "${GITHUB_OUTPUT}"
116
133
 
117
134
  - name: Validate runner architecture
118
135
  run: |
@@ -131,6 +148,7 @@ jobs:
131
148
  id: build
132
149
  run: |
133
150
  release_tag="${{ steps.release.outputs.tag }}"
151
+ release_version="${{ steps.release.outputs.version }}"
134
152
  release_dir="build/release/${ASSET_BASENAME}"
135
153
  wheelhouse_dir="${release_dir}/wheelhouse"
136
154
  dist_dir="build/release/dist"
@@ -158,6 +176,22 @@ jobs:
158
176
  project_wheel_name="$(basename "${project_wheel_path}")"
159
177
  project_source_distribution_path="${project_source_distributions[0]}"
160
178
  project_source_distribution_name="$(basename "${project_source_distribution_path}")"
179
+ case "${project_wheel_name}" in
180
+ src_auth_perms_sync-"${release_version}"-*.whl)
181
+ ;;
182
+ *)
183
+ echo "::error title=Wheel version mismatch::Expected wheel version ${release_version}, got '${project_wheel_name}'."
184
+ exit 1
185
+ ;;
186
+ esac
187
+ case "${project_source_distribution_name}" in
188
+ src_auth_perms_sync-"${release_version}".tar.gz)
189
+ ;;
190
+ *)
191
+ echo "::error title=Source distribution version mismatch::Expected source distribution version ${release_version}, got '${project_source_distribution_name}'."
192
+ exit 1
193
+ ;;
194
+ esac
161
195
  project_wheel_checksum_path="${project_wheel_path}.sha256"
162
196
  project_source_distribution_checksum_path="${project_source_distribution_path}.sha256"
163
197
  if [[ ! -f "${project_wheel_path}" ]]; then
@@ -165,18 +199,27 @@ jobs:
165
199
  exit 1
166
200
  fi
167
201
 
168
- uv export \
169
- --no-dev \
170
- --no-emit-project \
171
- --no-hashes \
172
- --no-header \
173
- --no-annotate \
174
- --frozen \
175
- --output-file "${requirements_file}"
202
+ dependency_metadata_dir="$(mktemp -d)"
203
+ git clone --no-hardlinks . "${dependency_metadata_dir}" >/dev/null
204
+ (
205
+ cd "${dependency_metadata_dir}"
206
+ git checkout --detach "${release_tag}" >/dev/null
207
+ mkdir -p "$(dirname "${requirements_file}")"
208
+ uv export \
209
+ --no-sources \
210
+ --no-dev \
211
+ --no-emit-project \
212
+ --no-hashes \
213
+ --no-header \
214
+ --no-annotate \
215
+ --output-file "${requirements_file}"
216
+ )
176
217
 
218
+ cp "${dependency_metadata_dir}/${requirements_file}" "${requirements_file}"
177
219
  cp "${requirements_file}" "${runtime_requirements_file}"
178
- if grep -q '^\./' "${runtime_requirements_file}"; then
220
+ if grep -Eq '(^-e[[:space:]]|^(\.\.?/)|(^|[[:space:]])file:| @ (\.\.?/|file:))' "${runtime_requirements_file}"; then
179
221
  echo "::error title=Unexpected local dependency::Runtime requirements must resolve from PyPI."
222
+ cat "${runtime_requirements_file}"
180
223
  exit 1
181
224
  fi
182
225
 
@@ -356,19 +399,21 @@ jobs:
356
399
 
357
400
  github-release:
358
401
  name: Publish GitHub release assets
359
- needs: [validate, wheelhouse]
402
+ needs: [release_ref, validate, wheelhouse]
360
403
  runs-on: ubuntu-24.04
404
+ permissions:
405
+ contents: write
361
406
 
362
407
  steps:
363
408
  - name: Download wheelhouse artifacts
364
- uses: actions/download-artifact@v7
409
+ uses: actions/download-artifact@v8
365
410
  with:
366
411
  pattern: src-auth-perms-sync-*
367
412
  path: release-assets
368
413
  merge-multiple: true
369
414
 
370
415
  - name: Download release notes
371
- uses: actions/download-artifact@v7
416
+ uses: actions/download-artifact@v8
372
417
  with:
373
418
  name: release-notes
374
419
  path: release-notes
@@ -377,8 +422,9 @@ jobs:
377
422
  env:
378
423
  GH_TOKEN: ${{ github.token }}
379
424
  GH_REPO: ${{ github.repository }}
425
+ RELEASE_TAG: ${{ needs.release_ref.outputs.tag }}
380
426
  run: |
381
- release_tag="${{ github.event.inputs.tag || github.ref_name }}"
427
+ release_tag="${RELEASE_TAG}"
382
428
  notes_path="$(find release-notes -name release-notes.md -print -quit)"
383
429
  mapfile -t release_assets < <(find release-assets -type f | sort)
384
430
 
@@ -415,7 +461,7 @@ jobs:
415
461
 
416
462
  steps:
417
463
  - name: Download built distribution
418
- uses: actions/download-artifact@v7
464
+ uses: actions/download-artifact@v8
419
465
  with:
420
466
  name: pypi-distributions
421
467
  path: dist
@@ -157,6 +157,7 @@ jobs:
157
157
  - name: Check out code
158
158
  uses: actions/checkout@v6
159
159
  with:
160
+ fetch-depth: 0
160
161
  persist-credentials: false
161
162
  ref: ${{ inputs.ref || github.ref }}
162
163
 
@@ -208,6 +209,7 @@ jobs:
208
209
  - name: Check out code
209
210
  uses: actions/checkout@v6
210
211
  with:
212
+ fetch-depth: 0
211
213
  persist-credentials: false
212
214
  ref: ${{ inputs.ref || github.ref }}
213
215
 
@@ -227,8 +229,8 @@ jobs:
227
229
  - name: Install uv
228
230
  run: python -m pip install "uv==${UV_VERSION}"
229
231
 
230
- - name: Build wheel
231
- run: uv build --wheel --out-dir dist --no-create-gitignore
232
+ - name: Build distributions
233
+ run: uv build --wheel --sdist --out-dir dist --no-create-gitignore
232
234
 
233
235
  - name: Smoke test installed wheel
234
236
  run: |
@@ -11,9 +11,11 @@ __pycache__
11
11
  *.gql
12
12
  *.py[cod]
13
13
  *.py[oc]
14
+ *.swp
14
15
  *.yaml
15
16
  build/
16
17
  dist/
18
+ logs*/
17
19
  logs/
18
20
  src-auth-perms-sync-runs/
19
21
  wheels/
@@ -44,50 +44,27 @@ uv run src-auth-perms-sync --restore backups/<source>/<run>/before.json
44
44
 
45
45
  ## Release process
46
46
 
47
- - The tagged source commit must already contain the package version it
48
- releases. Do not make the customer release workflow edit `pyproject.toml`.
49
- - Prepare the version bump on a branch. Set `VERSION`, then copy / paste:
50
- - As part of every release bump, find old release-version literals in
51
- `AGENTS.md`, `README.md`, and release snippets, and replace them with the
52
- new version where they are meant to stay current.
47
+ - Package versions are derived from Git tags through `hatch-vcs`.
48
+ - `pyproject.toml` must use `dynamic = ["version"]`; do not add a hard-coded
49
+ `project.version` for releases.
50
+ - The release tag must be `vMAJOR.MINOR.PATCH` and point at a commit reachable
51
+ from `origin/main`.
52
+ - The release workflow builds from the tag and checks that wheel and source
53
+ distribution filenames match the tag version before publishing.
54
+ - Do not make the release workflow edit `pyproject.toml` or `uv.lock`.
55
+ - Validate the remote head of `main` before tagging it:
53
56
 
54
57
  ```bash
55
58
  set -euo pipefail
56
59
 
57
- VERSION=0.2.1
58
- BRANCH="release-v${VERSION}"
60
+ VERSION_INPUT=<next-version>
61
+ VERSION="${VERSION_INPUT#v}"
59
62
 
63
+ [[ "${VERSION_INPUT}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]
60
64
  git fetch origin --tags --prune
61
65
  git switch main
62
66
  git pull --ff-only
63
- git switch -c "${BRANCH}"
64
-
65
- uv run python - "${VERSION}" <<'PY'
66
- from pathlib import Path
67
- import re
68
- import sys
69
-
70
- version = sys.argv[1]
71
- path = Path("pyproject.toml")
72
- text = path.read_text()
73
- new_text = re.sub(
74
- r'(?m)^version = "[^"]+"$',
75
- f'version = "{version}"',
76
- text,
77
- count=1,
78
- )
79
- if new_text == text:
80
- raise SystemExit("pyproject.toml version was not updated")
81
- path.write_text(new_text)
82
- PY
83
-
84
- uv lock
85
- ```
86
-
87
- - Validate the release candidate before opening / merging the PR:
88
-
89
- ```bash
90
- set -euo pipefail
67
+ test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
91
68
 
92
69
  uv lock --check
93
70
  actionlint
@@ -97,57 +74,24 @@ uv run pyright
97
74
  uv run python -m unittest discover -s tests
98
75
  uv run src-auth-perms-sync --help
99
76
  npx --yes markdownlint-cli2@0.22.1
100
- uv build --wheel --out-dir /tmp/src-auth-perms-sync-release-check --no-create-gitignore
77
+ uv build --wheel --sdist --out-dir /tmp/src-auth-perms-sync-release-check --no-create-gitignore
101
78
  rm -rf /tmp/src-auth-perms-sync-release-check
102
79
  ```
103
80
 
104
- - Commit, push, open the PR, wait for checks, then merge it. If review is
105
- required, stop after `gh pr checks` and ask for review before merging.
81
+ - Tag the remote head of `main` directly:
106
82
 
107
83
  ```bash
108
84
  set -euo pipefail
109
85
 
110
- VERSION=0.2.1
111
- BRANCH="release-v${VERSION}"
86
+ VERSION_INPUT=<next-version>
87
+ VERSION="${VERSION_INPUT#v}"
112
88
  GH_REPO="sourcegraph/src-auth-perms-sync"
113
89
 
114
- git add pyproject.toml uv.lock
115
- git commit -m "Release v${VERSION}"
116
- git push -u origin "${BRANCH}"
117
-
118
- gh pr create \
119
- --repo "${GH_REPO}" \
120
- --base main \
121
- --head "${BRANCH}" \
122
- --title "Release v${VERSION}" \
123
- --body "Bump src-auth-perms-sync package metadata to ${VERSION}."
124
-
125
- gh pr checks "${BRANCH}" --repo "${GH_REPO}" --watch --fail-fast
126
- gh pr merge "${BRANCH}" --repo "${GH_REPO}" --squash --delete-branch
127
- ```
128
-
129
- - Tag the merged `main` commit. Do not tag a feature branch commit.
130
-
131
- ```bash
132
- set -euo pipefail
133
-
134
- VERSION=0.2.1
135
-
90
+ [[ "${VERSION_INPUT}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]
136
91
  git fetch origin --tags --prune
137
- git switch main
138
- git pull --ff-only
139
- git tag "v${VERSION}"
92
+ MAIN_COMMIT="$(git rev-parse origin/main)"
93
+ git tag -a "v${VERSION}" "${MAIN_COMMIT}" -m "Release v${VERSION}"
140
94
  git push origin "v${VERSION}"
141
- ```
142
-
143
- - Watch the customer release workflow and confirm the GitHub release assets
144
- are uploaded:
145
-
146
- ```bash
147
- set -euo pipefail
148
-
149
- VERSION=0.2.1
150
- GH_REPO="sourcegraph/src-auth-perms-sync"
151
95
 
152
96
  RUN_ID="$(
153
97
  gh run list \
@@ -169,13 +113,13 @@ gh release view "v${VERSION}" --repo "${GH_REPO}"
169
113
  ```bash
170
114
  set -euo pipefail
171
115
 
172
- VERSION=0.2.1
116
+ VERSION_INPUT=<version-to-fix>
117
+ VERSION="${VERSION_INPUT#v}"
173
118
  GH_REPO="sourcegraph/src-auth-perms-sync"
174
119
 
120
+ [[ "${VERSION_INPUT}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]
175
121
  git fetch origin --tags --prune
176
- git switch main
177
- git pull --ff-only
178
- git tag -f "v${VERSION}" origin/main
122
+ git tag -f -a "v${VERSION}" origin/main -m "Release v${VERSION}"
179
123
  git push origin "refs/tags/v${VERSION}" --force
180
124
 
181
125
  RUN_ID="$(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: src-auth-perms-sync
3
- Version: 0.2.2
3
+ Version: 0.3.1
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
@@ -18,11 +18,11 @@ Classifier: Typing :: Typed
18
18
  Requires-Python: >=3.11
19
19
  Requires-Dist: json5>=0.14.0
20
20
  Requires-Dist: pyyaml>=6.0.3
21
- Requires-Dist: src-py-lib==0.1.5
21
+ Requires-Dist: src-py-lib[otel]==0.2.1
22
22
  Description-Content-Type: text/markdown
23
23
 
24
24
  # src-auth-perms-sync
25
- <!-- HUMAN-MAINTAINED — DO NOT EDIT THIS FILE -->
25
+ <!-- HUMAN-MAINTAINED SECTION START — DO NOT EDIT THIS SECTION -->
26
26
 
27
27
  src-auth-perms-sync automates Sourcegraph's Explicit Permissions GraphQL API,
28
28
  setting user-to-repo permissions based on mapping rules, for example:
@@ -41,8 +41,8 @@ Feel free to open issues or PRs, but responses are best effort.
41
41
 
42
42
  - Release versions are `major.minor.patch`
43
43
  - Because this project is still major version 0:
44
- - Minor version updates are breaking changes
45
- - Patch version updates are not breaking changes
44
+ - Minor version updates are probably breaking changes
45
+ - Patch version updates are probably not breaking changes
46
46
 
47
47
  ## Principles
48
48
 
@@ -54,7 +54,7 @@ Feel free to open issues or PRs, but responses are best effort.
54
54
  map casts a smaller net of users / repos. This can result in more maps,
55
55
  but they will be easier to understand and trust.
56
56
 
57
- - Backup files are saved in `src-auth-perms-sync-runs/<endpoint>/backups/`,
57
+ - Backup files are saved in `src-auth-perms-sync-runs/<src_endpoint>/runs/<run>/`,
58
58
  unless the `--no-backup` arg is provided,
59
59
  so customers can review the changes made over time,
60
60
  and restore to a specific backup file, if needed
@@ -68,7 +68,10 @@ Feel free to open issues or PRs, but responses are best effort.
68
68
 
69
69
  - One installation of this script can apply separate `maps.yaml` files on
70
70
  separate Sourcegraph instances
71
- - Be sure to specify the path to the correct `maps.yaml` file for each run
71
+ - By default, each Sourcegraph instance gets its own generated `maps.yaml`
72
+ under `src-auth-perms-sync-runs/<src_endpoint>/`
73
+ - If you pass `--maps-path`, relative paths are resolved from your current
74
+ working directory
72
75
  - Set the `SRC_ENDPOINT` and `SRC_ACCESS_TOKEN` environment variables correctly for each run
73
76
 
74
77
  ## Prerequisites
@@ -112,102 +115,126 @@ Feel free to open issues or PRs, but responses are best effort.
112
115
 
113
116
  ## Install
114
117
 
115
- - Requires Python 3.11
118
+ - Requires Python >= 3.11
116
119
  - Recommended: Use a Python virtual environment
117
120
 
118
- ### Install from a GitHub Release
119
-
120
- Use this when the VM can reach GitHub and PyPI during install:
121
+ ### Install from PyPI
121
122
 
122
123
  ```bash
123
- python3.11 -m venv .venv
124
- . .venv/bin/activate
125
- pip install \
126
- "https://github.com/sourcegraph/src-auth-perms-sync/releases/download/v0.1.0/src_auth_perms_sync-0.1.0-py3-none-any.whl"
127
- ```
124
+ # Set up virtual environment
125
+ python3 -m venv .venv
126
+ source .venv/bin/activate
127
+ python -m pip install --upgrade pip
128
+
129
+ # Install package from PyPI
130
+ python -m pip install src-auth-perms-sync
128
131
 
129
- ### Restricted/offline install from a GitHub Release
132
+ # Run the CLI
133
+ src-auth-perms-sync --help
134
+ ```
130
135
 
131
- Use this when the VM cannot reach package indexes during install
136
+ ### Restricted / offline install from a GitHub Release
132
137
 
133
- Download the .tar.gz file from the GitHub release
138
+ Download the .tar.gz file from [a GitHub release](https://github.com/sourcegraph/src-auth-perms-sync/releases)
134
139
 
135
140
  ```bash
136
141
  tar -xzf src-auth-perms-sync-linux-x64.tar.gz
137
- python3.11 -m venv .venv
138
- . .venv/bin/activate
142
+
139
143
  pip install --no-index --find-links ./wheelhouse src-auth-perms-sync
144
+
145
+ # Run the CLI
146
+ src-auth-perms-sync --help
140
147
  ```
141
148
 
142
- After either install method, run the CLI from the activated virtual environment:
149
+ ### Import into your own Python script
143
150
 
144
- ```bash
145
- src-auth-perms-sync --help
151
+ ```python
152
+ from pathlib import Path
153
+
154
+ import src_auth_perms_sync as src
155
+
156
+ config = src.Config(
157
+ src_endpoint="https://sourcegraph.example.com",
158
+ src_access_token="sgp_...",
159
+ maps_path=Path("/absolute/path/to/maps.yaml"),
160
+ apply=False, # Dry run (default), set to True to make changes
161
+ )
162
+
163
+ succeeded = src.Set(config)
164
+
165
+ # Other command wrappers:
166
+ # succeeded = src.Get(config)
167
+ # succeeded = src.Restore(config)
168
+ # succeeded = src.SyncSamlOrgs(config)
146
169
  ```
147
170
 
148
171
  ## Inputs
149
172
 
150
- - Environment variables
173
+ - Environment variables (CLI), or src.Config args (Python import)
151
174
  - `SRC_ENDPOINT`
152
175
  - `SRC_ACCESS_TOKEN` from a user with site-admin perms
153
- - Supplied via the environment or a `.env` file
154
176
  - See [.env.example](./.env.example)
155
177
 
156
- - YAML maps file `src-auth-perms-sync-runs/<endpoint>/maps.yaml`
178
+ - YAML maps file
179
+ - By default: `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
180
+ - Or pass `--maps-path ./path/to/maps.yaml`
157
181
  - A list of mapping rules
158
182
  - Each mapping rule takes
159
- - A list of filters for users
160
- - A list of filters for repos
183
+ - A map of filters for users
184
+ - A map of filters for repos
161
185
  - See [maps-example.yaml](./maps-example.yaml)
162
- - An empty maps.yaml file is created for you on the first --get run
186
+ - An empty maps.yaml file is created for you on the first `get` run
163
187
 
164
188
  ## Usage: Permission sync
165
189
 
166
190
  1. **Get auth providers and code hosts**
167
191
 
168
192
  ```bash
169
- uv run src-auth-perms-sync [--get]
193
+ src-auth-perms-sync get
170
194
  ```
171
195
 
172
196
  - Queries the Sourcegraph instance for auth providers and code host connections
173
197
  - Writes generated reference files `auth-providers.yaml` and `code-hosts.yaml` under
174
- `src-auth-perms-sync-runs/<endpoint>/`
198
+ `src-auth-perms-sync-runs/<src_endpoint>/`
175
199
  - Creates an empty `maps.yaml` if it doesn't exist
176
- - Runs by default when no command is selected
177
200
 
178
201
  2. **Configure mapping rules**
179
202
 
180
- - Edit `maps.yaml`
203
+ - Edit `src-auth-perms-sync-runs/<src_endpoint>/maps.yaml`
181
204
  - Add mapping rules under the `maps:` top level key
182
205
  - See [maps-example.yaml](./maps-example.yaml)
183
206
 
184
207
  3. **Set: Dry run**
185
208
 
186
209
  ```bash
187
- uv run src-auth-perms-sync --set maps.yaml --full
210
+ src-auth-perms-sync set --full
188
211
  ```
189
212
 
190
213
  4. **Set: Apply**
191
214
 
192
215
  ```bash
193
- uv run src-auth-perms-sync --set maps.yaml --full --apply
216
+ src-auth-perms-sync set --full --apply
194
217
  ```
195
218
 
219
+ - To use a maps file outside the generated endpoint directory, pass an
220
+ explicit path, for example `--maps-path ./maps.yaml`
221
+
196
222
  5. **Restore: Dry run**
197
223
 
198
224
  ```bash
199
- uv run src-auth-perms-sync \
200
- --restore backups/maps.yaml/2026-04-27-08-24-25-set-apply/before.json
225
+ src-auth-perms-sync restore \
226
+ --restore-path src-auth-perms-sync-runs/<src_endpoint>/runs/<run>/before.json
201
227
  ```
202
228
 
203
229
  - Roll back the explicit-permissions state on the
204
230
  instance to match a previously captured snapshot
231
+ - Relative `--restore-path` values are resolved from your current working directory
205
232
 
206
233
  6. **Restore: Apply**
207
234
 
208
235
  ```bash
209
- uv run src-auth-perms-sync \
210
- --restore backups/maps.yaml/2026-04-27-08-24-25-set-apply/before.json \
236
+ src-auth-perms-sync restore \
237
+ --restore-path src-auth-perms-sync-runs/<src_endpoint>/runs/<run>/before.json \
211
238
  --apply
212
239
  ```
213
240
 
@@ -216,7 +243,7 @@ src-auth-perms-sync --help
216
243
  1. **Get user and org metadata**
217
244
 
218
245
  ```bash
219
- uv run src-auth-perms-sync --sync-saml-orgs
246
+ src-auth-perms-sync sync-saml-orgs
220
247
  ```
221
248
 
222
249
  - Queries the Sourcegraph instance for auth providers, users, users' SAML groups, and orgs
@@ -225,45 +252,45 @@ src-auth-perms-sync --help
225
252
  2. **Apply org sync**
226
253
 
227
254
  ```bash
228
- uv run src-auth-perms-sync --sync-saml-orgs --apply
255
+ src-auth-perms-sync sync-saml-orgs --apply
229
256
  ```
230
257
 
231
258
  - Creates the orgs if they don't exist, and sync the members from the SAML groups to the orgs
232
- - `--sync-saml-orgs` can also be added to a `--set` run, to run both at the same time
259
+ - `--sync-saml-orgs` can also be added to a `set` run, to run both at the same time
233
260
 
234
261
  ## Options
235
262
 
236
- Run `uv run src-auth-perms-sync --help` for options
263
+ Run `src-auth-perms-sync --help` for options
237
264
 
238
265
  ## File tree
239
266
 
240
267
  ```text
241
- src-auth-perms-sync-runs/endpoint/
268
+ src-auth-perms-sync-runs/<src_endpoint>/
242
269
  ├── auth-providers.yaml
243
270
  ├── code-hosts.yaml
244
271
  ├── maps.yaml
245
272
  └── runs
246
273
  └── timestamp-command
247
- ├── after.json
248
274
  ├── before.json
275
+ ├── after.json
249
276
  ├── diff.json
250
277
  ├── log.json
251
278
  └── maps.yaml
252
279
  ```
253
280
 
254
281
  - The `src-auth-perms-sync-runs` dir is created under your current working directory
255
- - The `endpoint` dir is created with the hostname from `SRC_ENDPOINT`
282
+ - The `<src_endpoint>` dir is created with the hostname from `SRC_ENDPOINT`
256
283
  - If `maps.yaml` doesn't exist already, it'll be created for you
257
- - `auth-providers.yaml` and `code-hosts.yaml` are created / replaced by the `--get` command,
284
+ - `auth-providers.yaml` and `code-hosts.yaml` are created / replaced by the `get` command,
258
285
  for you to copy values from, to use in your `maps.yaml`
259
- - Only one `maps.yaml` file can be used at a time per Sourcegraph instance, as each `--set --apply`
286
+ - Only one `maps.yaml` file can be used at a time per Sourcegraph instance, as each `set --apply`
260
287
  command resets the state on the Sourcegraph instance to the `maps.yaml` file which was used
261
288
  - Each run of the script creates a new `timestamp-command` dir under the `runs` dir, with:
289
+ - A `before.json` file, capturing the before state, which can be used in a restore run
262
290
  - A log file
263
291
  - A backup copy of the `maps.yaml` file which was used in that run
264
- - A `before.json` file, capturing the before state, which can be restored from
265
292
  - Runs using `--apply` also create
266
293
  - An `after.json` file, capturing the new state
267
294
  - A `diff.json` file, a shorter, reviewable file containing the diffs between before and after
268
295
 
269
- <!-- HUMAN-MAINTAINED — DO NOT EDIT THIS FILE -->
296
+ <!-- HUMAN-MAINTAINED SECTION END — DO NOT EDIT ABOVE -->