Skip to content

Commit b9f2f59

Browse files
committed
Update setup guide with post-setup requirements and fixes; fix test install order
1 parent 48d84f8 commit b9f2f59

File tree

3 files changed

+60
-31
lines changed

3 files changed

+60
-31
lines changed

.Pipelines/ADO-PUBLISH-SETUP.md

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ Every publish requires explicitly entering a version and selecting a destination
2525
| **Validate** | always | asserts `packageVersion` matches `msal/sku.py` |
2626
| **CI** (tests on Py 3.9–3.13) | after Validate ||
2727
| **Build** (sdist + wheel) | after CI | dist artifact |
28-
| **PublishMSALPython** | `publishTarget = MSAL-Python` | test.pypi.org |
29-
| **PublishPyPI** | `publishTarget = pypi` | PyPI (production) |
28+
| **PublishMSALPython** | `publishTarget = test.pypi.org (Preview / RC)` | test.pypi.org |
29+
| **PublishPyPI** | `publishTarget = pypi.org (Production)` | PyPI (production) |
3030

3131
---
3232

@@ -46,11 +46,16 @@ Every publish requires explicitly entering a version and selecting a destination
4646

4747
1. In your ADO project go to **Project Settings → Service connections → New service connection**.
4848
2. Choose **GitHub** and click **Next**.
49-
3. Select **GitHub App** (recommended) or **Personal Access Token**.
50-
- GitHub App: follow the OAuth flow to install the ADO GitHub App on the
51-
`AzureAD` organization and grant repository access to
52-
`microsoft-authentication-library-for-python`.
53-
- PAT: create a GitHub PAT with `repo` scope and paste it here.
49+
3. Under **Authentication**, select **Grant authorization** (OAuth) — do **not** use Personal Access Token.
50+
- Click **Authorize** — a GitHub OAuth popup will open.
51+
- Sign in with a GitHub account that has admin rights on the `AzureAD` organization.
52+
- Grant access to `microsoft-authentication-library-for-python`.
53+
- This installs the Azure Pipelines GitHub App and enables webhook and repository listing.
54+
55+
> **Why OAuth and not PAT:** PAT-based connections cannot install the GitHub webhook
56+
> required for pipeline creation via CLI or API. The OAuth/GitHub App flow installs the
57+
> webhook using the browser's authenticated GitHub session.
58+
5459
4. Set **Service connection name**: `github-msal-python`
5560
5. Check **Grant access permission to all pipelines**, click **Save**.
5661

@@ -70,8 +75,8 @@ for external registries.
7075
|-------|-------|
7176
| **Twine repository URL** | `https://test.pypi.org/legacy/` |
7277
| **EndpointName** (`-r` value) | `MSAL-Test-Python-Upload` |
73-
| **Username** | `__token__` |
74-
| **Password** | *(your test.pypi.org API token, e.g. `pypi-AgA...`)* |
78+
| **Authentication method** | **Authentication Token** |
79+
| **Token** | *(your test.pypi.org API token, full value including `pypi-` prefix)* |
7580
| **Service connection name** | `MSAL-Test-Python-Upload` |
7681
4. Check **Grant access permission to all pipelines**, click **Save**.
7782

@@ -84,8 +89,8 @@ for external registries.
8489
|-------|-------|
8590
| **Twine repository URL** | `https://upload.pypi.org/legacy/` |
8691
| **EndpointName** (`-r` value) | `MSAL-Prod-Python-Upload` |
87-
| **Username** | `__token__` |
88-
| **Password** | *(your PyPI API token)* |
92+
| **Authentication method** | **Authentication Token** |
93+
| **Token** | *(your PyPI API token, full value including `pypi-` prefix)* |
8994
| **Service connection name** | `MSAL-Prod-Python-Upload` |
9095
4. Check **Grant access permission to all pipelines**, click **Save**.
9196

@@ -151,29 +156,36 @@ This pipeline is **always manually queued**. Both fields are required — the Va
151156

152157
| Parameter | Required | Description | Example values |
153158
|-----------|----------|-------------|----------------|
154-
| **Package version to publish** | ✅ Yes | Must exactly match `msal/sku.py __version__`. | `1.36.0` (release), `1.36.0rc1` (preview) |
155-
| **Publish target** | ✅ Yes | Explicit destination — no auto-routing. | `MSAL-Python` (test.pypi.org) or `pypi` (production) |
159+
| **Package version to publish** | Yes | Must exactly match `msal/sku.py __version__`. PEP 440 format only — no `-Preview` suffix. | `1.36.0` (release), `1.36.0rc1` (RC), `1.36.0b1` (beta) |
160+
| **Publish target** | Yes | Explicit destination — no auto-routing. | `test.pypi.org (Preview / RC)` or `pypi.org (Production)` |
161+
162+
> **Version format:** PyPI enforces [PEP 440](https://peps.python.org/pep-0440/). Versions with `-` (e.g. `1.36.0-Preview`) are rejected. Use `rc1`, `b1`, or `a1` suffixes instead.
163+
164+
> **Version must be in sync:** Before queuing, update `msal/sku.py __version__` to the target version and push the change. The Validate stage checks the value on the branch the run is sourced from, not the pipeline default branch.
156165
157166
---
158167

159168
## Step 8 — End-to-End Release Walkthrough
160169

161170
### Publishing a preview / release candidate to test.pypi.org
162171

163-
1. Set `msal/sku.py __version__ = "1.36.0rc1"`
164-
2. Go to **Pipelines → Run pipeline**
165-
3. Enter `packageVersion = 1.36.0rc1`, select `publishTarget = MSAL-Python`
166-
4. Click **Run** — pipeline runs: Validate → CI → Build → PublishMSALPython
167-
5. Verify at <https://test.pypi.org/project/msal/>
172+
1. Set `msal/sku.py __version__ = "1.36.0rc1"` and push the change
173+
2. Go to **Pipelines → MSAL-Python · Publish → Run pipeline**
174+
3. Select the branch/tag to run from (e.g. the release branch)
175+
4. Enter **Package version to publish**: `1.36.0rc1`
176+
5. Select **Publish target**: `test.pypi.org (Preview / RC)`
177+
6. Click **Run** — pipeline runs: Validate → CI → Build → Publish to test.pypi.org
178+
7. Verify at <https://test.pypi.org/project/msal/>
168179

169180
### Publishing a production release to PyPI
170181

171-
1. Set `msal/sku.py __version__ = "1.36.0"` and merge to the release branch
172-
2. Go to **Pipelines → Run pipeline**
173-
3. Enter `packageVersion = 1.36.0`, select `publishTarget = pypi`
174-
4. Click **Run** — pipeline runs: Validate → CI → Build → PublishPyPI (approval gate)
175-
5. Go to **Pipelines → Environments → MSAL-Python-Release** and approve the deployment
176-
6. Verify: `pip install msal==1.36.0` or check <https://pypi.org/project/msal/>
182+
1. Set `msal/sku.py __version__ = "1.36.0"` and push to the release branch
183+
2. Go to **Pipelines → MSAL-Python · Publish → Run pipeline**
184+
3. Select the release branch
185+
4. Enter **Package version to publish**: `1.36.0`
186+
5. Select **Publish target**: `pypi.org (Production)`
187+
6. Click **Run** — pipeline runs: Validate → CI → Build → Publish to PyPI (Production)
188+
7. Verify: `pip install msal==1.36.0` or check <https://pypi.org/project/msal/>
177189

178190
## Pipeline Trigger Reference
179191

@@ -189,13 +201,30 @@ Manual queue (publishTarget = pypi)
189201

190202
---
191203

204+
## Known Requirements
205+
206+
The following requirements were identified during initial setup and testing:
207+
208+
- The GitHub service connection **must** be created via OAuth (Grant authorization) in the ADO UI, not via CLI or PAT. The CLI `az pipelines create` command requires webhook installation on the GitHub repo, which requires org admin rights not available to service accounts.
209+
- The pipeline **must** be created via the ADO REST API (`/_apis/build/definitions`) or UI — not via `az pipelines create` — when using an OAuth GitHub service connection without org-level admin rights.
210+
- The `msal/sku.py __version__` must be updated and pushed to the source branch **before** the pipeline run is queued. The Validate stage reads the file from the checked-out branch at runtime.
211+
- The `requirements.txt` file includes `-e .` which causes pip to install `msal` from PyPI as a transitive dependency of `azure-identity`, overwriting the local editable install. The template handles this by removing the `-e .` line and reinstalling the local package last with `--no-deps`.
212+
- The `1.35.1` version bump (hotfix) was released from `origin/release-1.35.0` and was never merged back into `dev`. Before the next release from `dev`, this should be backfilled via PR: `https://github.com/AzureAD/microsoft-authentication-library-for-python/compare/dev...release-1.35.0`
213+
214+
---
215+
192216
## Troubleshooting
193217

194218
| Symptom | Likely cause | Fix |
195219
|---------|-------------|-----|
196220
| `403` on twine upload | Token expired or wrong scope | Regenerate API token on pypi.org; update the service connection |
197221
| `File already exists` error | Version already published; PyPI does not allow overwriting | Bump version in `msal/sku.py` |
198-
| Pipeline not triggered by tag | ADO only picks up tags after the pipeline is saved with the `tags:` trigger | Re-save the pipeline in ADO after adding the trigger |
222+
| Validate stage: `msal/sku.py ''` (empty version) | Python import silently failed | The template uses `grep`/`sed` to read the version — verify `msal/sku.py` contains a `__version__ = "..."` line |
223+
| Validate stage: version mismatch | `sku.py` on the source branch doesn't match the parameter entered | Update `msal/sku.py` on the branch the run is sourced from, not just the pipeline default branch |
224+
| Tests: collection failure across all modules | PyPI `msal` installed over the local editable install | Ensure the template installs local package last with `--no-deps` |
225+
| `az pipelines create` fails with webhook error | GitHub service connection PAT/account lacks org admin rights | Create the pipeline via the ADO UI using a browser session with org admin GitHub access |
226+
| Pipeline creation fails: `Value cannot be null. Parameter name: Connection` | GitHub SC ID is wrong or SC was recreated | Re-query the SC ID with `az devops service-endpoint list` and use the current ID |
227+
| Service connection shows `Authentication: PersonalAccessToken` | SC was created via CLI with a PAT | Delete and recreate via UI using OAuth (Grant authorization) so repos are enumerable |
199228
| `TwineAuthenticate` says endpoint not found | Service connection name mismatch | Ensure `pythonUploadServiceConnection` value exactly matches the service connection name |
200229

201230
---

.Pipelines/pipeline-publish.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ variables:
4242
# ══════════════════════════════════════════════════════════════════════════════
4343
stages:
4444
- stage: Validate
45-
displayName: '🔍 Validate version'
45+
displayName: 'Validate version'
4646
jobs:
4747
- job: ValidateVersion
4848
displayName: 'Check version matches source'
@@ -74,7 +74,7 @@ stages:
7474
# Stage 2 · CI — run the full test matrix
7575
# ══════════════════════════════════════════════════════════════════════════════
7676
- stage: CI
77-
displayName: '🧪 Run tests'
77+
displayName: 'Run tests'
7878
dependsOn: Validate
7979
condition: succeeded()
8080
jobs:
@@ -101,7 +101,7 @@ stages:
101101
# Stage 3 · Build — compile sdist + wheel (single Python version)
102102
# ══════════════════════════════════════════════════════════════════════════════
103103
- stage: Build
104-
displayName: '📦 Build package'
104+
displayName: 'Build package'
105105
dependsOn: CI
106106
condition: succeeded()
107107
jobs:
@@ -120,7 +120,7 @@ stages:
120120
# Runs when: publishTarget == 'MSAL-Python'
121121
# ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
122122
- stage: PublishMSALPython
123-
displayName: '🚀 Publish to test.pypi.org (Preview)'
123+
displayName: 'Publish to test.pypi.org (Preview)'
124124
dependsOn: Build
125125
condition: >
126126
and(
@@ -151,7 +151,7 @@ stages:
151151
# Runs when: publishTarget == 'pypi'
152152
# ══════════════════════════════════════════════════════════════════════════════
153153
- stage: PublishPyPI
154-
displayName: '🚀 Publish to PyPI (Production)'
154+
displayName: 'Publish to PyPI (Production)'
155155
dependsOn: Build
156156
condition: >
157157
and(

.Pipelines/template-run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ steps:
1717

1818
- script: |
1919
python -m pip install --upgrade pip
20-
pip install -e .
2120
grep -v '^-e' requirements.txt | pip install -r /dev/stdin
21+
pip install -e . --no-deps
2222
displayName: 'Install dependencies'
2323

2424
# Use bash: (not script:) so set -o pipefail works — script: uses /bin/sh on Linux

0 commit comments

Comments
 (0)