|
3 | 3 | # Unified pipeline stages template for the msal Python package. |
4 | 4 | # |
5 | 5 | # Called from: |
6 | | -# pipeline-publish.yml — release build (SDL scans + test matrix) |
7 | | -# azure-pipelines.yml — PR gate and post-merge CI |
| 6 | +# pipeline-publish.yml — release build (runPublish: true) |
| 7 | +# azure-pipelines.yml — PR gate and post-merge CI (runPublish: false) |
8 | 8 | # |
9 | | -# Package publishing is handled via the ESRP release path (not in this template). |
| 9 | +# Parameters: |
| 10 | +# packageVersion - Version to validate against msal/sku.py |
| 11 | +# Required when runPublish is true; unused otherwise. |
| 12 | +# publishTarget - 'test.pypi.org (Preview / RC)' or 'pypi.org (Pre-ESRP)' |
| 13 | +# Required when runPublish is true; unused otherwise. |
| 14 | +# runPublish - When true: also run Validate, Build, and Publish stages. |
| 15 | +# When false (PR / merge builds): only PreBuildCheck + CI run. |
10 | 16 | # |
11 | 17 | # Stage flow: |
12 | 18 | # |
13 | | -# PreBuildCheck ─► CI |
| 19 | +# runPublish: true → PreBuildCheck ─► Validate ─► CI ─► Build ─► PublishMSALPython |
| 20 | +# └─► PublishPyPI |
| 21 | +# runPublish: false → PreBuildCheck ─► CI (Validate / Build / Publish are skipped) |
| 22 | + |
| 23 | +parameters: |
| 24 | +- name: packageVersion |
| 25 | + type: string |
| 26 | + default: '' |
| 27 | +- name: publishTarget |
| 28 | + type: string |
| 29 | + default: '' |
| 30 | +- name: runPublish |
| 31 | + type: boolean |
| 32 | + default: false |
14 | 33 |
|
15 | 34 | stages: |
16 | 35 |
|
@@ -53,12 +72,55 @@ stages: |
53 | 72 | GdnBreakGdnToolPoliCheck: true |
54 | 73 |
|
55 | 74 | # ══════════════════════════════════════════════════════════════════════════════ |
56 | | -# Stage 1 · CI — run the full test matrix across all supported Python versions. |
| 75 | +# Stage 1 · Validate — verify packageVersion matches msal/sku.py __version__ |
| 76 | +# Skipped when runPublish is false (PR / merge builds). |
| 77 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 78 | +- stage: Validate |
| 79 | + displayName: 'Validate version' |
| 80 | + dependsOn: PreBuildCheck |
| 81 | + condition: and(${{ parameters.runPublish }}, eq(dependencies.PreBuildCheck.result, 'Succeeded')) |
| 82 | + jobs: |
| 83 | + - job: ValidateVersion |
| 84 | + displayName: 'Check version matches source' |
| 85 | + pool: |
| 86 | + vmImage: ubuntu-latest |
| 87 | + steps: |
| 88 | + - task: UsePythonVersion@0 |
| 89 | + inputs: |
| 90 | + versionSpec: '3.12' |
| 91 | + displayName: 'Set up Python' |
| 92 | + |
| 93 | + - bash: | |
| 94 | + PARAM_VER="${{ parameters.packageVersion }}" |
| 95 | + SKU_VER=$(grep '__version__' msal/sku.py | sed 's/.*"\(.*\)".*/\1/') |
| 96 | +
|
| 97 | + if [ -z "$PARAM_VER" ]; then |
| 98 | + echo "##vso[task.logissue type=error]packageVersion is required. Enter the version to publish (must match msal/sku.py __version__)." |
| 99 | + exit 1 |
| 100 | + elif [ "$PARAM_VER" != "$SKU_VER" ]; then |
| 101 | + echo "##vso[task.logissue type=error]Version mismatch: parameter '$PARAM_VER' != msal/sku.py '$SKU_VER'" |
| 102 | + echo "Update msal/sku.py __version__ to match the packageVersion parameter, or correct the parameter." |
| 103 | + exit 1 |
| 104 | + else |
| 105 | + echo "Version validated: $PARAM_VER" |
| 106 | + fi |
| 107 | + displayName: 'Verify version parameter matches msal/sku.py' |
| 108 | +
|
| 109 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 110 | +# Stage 2 · CI — run the full test matrix across all supported Python versions. |
| 111 | +# Always runs. Waits for Validate when runPublish is true; |
| 112 | +# runs immediately when Validate is skipped (PR / merge builds). |
57 | 113 | # ══════════════════════════════════════════════════════════════════════════════ |
58 | 114 | - stage: CI |
59 | 115 | displayName: 'Run tests' |
60 | | - dependsOn: PreBuildCheck |
61 | | - condition: eq(dependencies.PreBuildCheck.result, 'Succeeded') |
| 116 | + dependsOn: |
| 117 | + - PreBuildCheck |
| 118 | + - Validate |
| 119 | + condition: | |
| 120 | + and( |
| 121 | + eq(dependencies.PreBuildCheck.result, 'Succeeded'), |
| 122 | + in(dependencies.Validate.result, 'Succeeded', 'Skipped') |
| 123 | + ) |
62 | 124 | jobs: |
63 | 125 | - job: Test |
64 | 126 | displayName: 'Run unit tests' |
@@ -130,7 +192,12 @@ stages: |
130 | 192 |
|
131 | 193 | - bash: rm -f "$(Agent.TempDirectory)/lab-auth.pfx" |
132 | 194 | displayName: 'Clean up lab certificate' |
133 | | - condition: and(always(), ne(variables['LAB_APP_CLIENT_ID'], '')) |
| 195 | + condition: always() |
| 196 | + |
| 197 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 198 | +# Stage 3 · Build — build sdist + wheel (release only) |
| 199 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 200 | +- stage: Build |
134 | 201 | displayName: 'Build package' |
135 | 202 | dependsOn: CI |
136 | 203 | condition: and(eq(dependencies.CI.result, 'Succeeded'), eq(${{ parameters.runPublish }}, true)) |
@@ -166,6 +233,7 @@ stages: |
166 | 233 | # ══════════════════════════════════════════════════════════════════════════════ |
167 | 234 | # Stage 4a · Publish to test.pypi.org (Preview / RC) |
168 | 235 | # Runs when: runPublish is true AND publishTarget == 'test.pypi.org (Preview / RC)' |
| 236 | +# Note: requires MSAL-Test-Python-Upload SC in ADO (pending test.pypi.org token) |
169 | 237 | # ══════════════════════════════════════════════════════════════════════════════ |
170 | 238 | - stage: PublishMSALPython |
171 | 239 | displayName: 'Publish to test.pypi.org (Preview)' |
@@ -216,16 +284,16 @@ stages: |
216 | 284 | displayName: 'Upload to MSAL-Test-Python-Upload (skip existing)' |
217 | 285 |
|
218 | 286 | # ══════════════════════════════════════════════════════════════════════════════ |
219 | | -# Stage 4b · Publish to PyPI (Production) |
220 | | -# Runs when: runPublish is true AND publishTarget == 'pypi.org (Production)' |
| 287 | +# Stage 4b · Publish to PyPI (ESRP Production) |
| 288 | +# Runs when: runPublish is true AND publishTarget == 'pypi.org (ESRP Production)' |
221 | 289 | # ══════════════════════════════════════════════════════════════════════════════ |
222 | 290 | - stage: PublishPyPI |
223 | | - displayName: 'Publish to PyPI (Production)' |
| 291 | + displayName: 'Publish to PyPI (ESRP Production)' |
224 | 292 | dependsOn: Build |
225 | 293 | condition: > |
226 | 294 | and( |
227 | 295 | eq(dependencies.Build.result, 'Succeeded'), |
228 | | - eq('${{ parameters.publishTarget }}', 'pypi.org (Production)') |
| 296 | + eq('${{ parameters.publishTarget }}', 'pypi.org (ESRP Production)') |
229 | 297 | ) |
230 | 298 | jobs: |
231 | 299 | - deployment: DeployPyPI |
@@ -265,4 +333,4 @@ stages: |
265 | 333 | -r "MSAL-Prod-Python-Upload" \ |
266 | 334 | --config-file $(PYPIRC_PATH) \ |
267 | 335 | $(Pipeline.Workspace)/python-dist/* |
268 | | - displayName: 'Upload to MSAL-Prod-Python-Upload' |
| 336 | + displayName: 'Upload to PyPI (ESRP Production)' |
0 commit comments