Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ea16799
Add ADO pipeline hyperlinks to CI-AND-RELEASE-PIPELINES.md
RyAuld Mar 31, 2026
5eaf533
Add Python 3.8 to ADO CI matrix
RyAuld Mar 31, 2026
bd41633
Move benchmarks from GH Actions to ADO; remove cb job from GH workflow
RyAuld Mar 31, 2026
10814c4
Remove GH Actions CI/CD workflow — tests and publish fully covered by…
RyAuld Mar 31, 2026
a8154b4
Address Copilot review: fix benchmark branch condition, switch to Pub…
RyAuld Mar 31, 2026
6107946
Fix: move retries from job to retryCountOnTaskFailure on pytest step …
RyAuld Mar 31, 2026
1bd1f0a
Address Copilot review: wire tsaConfig.json to PublishSecurityAnalysi…
RyAuld Mar 31, 2026
a1a9cff
Merge origin/dev into RyAuld/update-pipeline-doc-links; keep deletion…
RyAuld May 8, 2026
de03197
Move cryptography version-gating tests to warning-only step
RyAuld May 8, 2026
358e010
Add Benchmark stage to pipeline documentation
RyAuld May 8, 2026
cfbd078
Address Copilot review: benchmark skip, pytest install, artifact guard
RyAuld May 8, 2026
30bd106
Fix: allow Build/Publish stages to proceed on SucceededWithIssues
RyAuld May 14, 2026
970bb02
Upgrade EsrpRelease task from v9 to v12
RyAuld May 15, 2026
02c6b07
updated for e2e tests
4gust May 21, 2026
180a6ff
merged dev
4gust May 21, 2026
faaa5fe
updated the pipeline runtime and updated the version for sku for rc
4gust May 21, 2026
8203f47
Update template-pipeline-stages.yml
4gust May 21, 2026
5cbdc58
Update template-pipeline-stages.yml
4gust May 21, 2026
6e3e6ad
Resolved comments
4gust May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions .Pipelines/CI-AND-RELEASE-PIPELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ including what each pipeline does, when it runs, and how to trigger a release.

## Pipeline Files

| File | Purpose |
|------|---------|
| [`azure-pipelines.yml`](../azure-pipelines.yml) | PR gate and post-merge CI — calls the shared template with `runPublish: false` |
| [`pipeline-publish.yml`](pipeline-publish.yml) | Release pipeline — manually queued, builds and publishes to PyPI |
| [`template-pipeline-stages.yml`](template-pipeline-stages.yml) | Shared stages template — PreBuildCheck, Validate, and CI stages reused by both pipelines |
| [`credscan-exclusion.json`](credscan-exclusion.json) | CredScan suppression file for known test fixtures |
| File | ADO Pipeline | Purpose |
|------|-------------|---------|
| [`azure-pipelines.yml`](../azure-pipelines.yml) | [MSAL.Python-PR-OneBranch-Official (3064)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3064) | PR gate, post-merge CI, and performance benchmarks — calls the shared template with `runPublish: false`; runs benchmarks on post-merge pushes to `dev` |
| [`pipeline-publish.yml`](pipeline-publish.yml) | [MSAL.Python-Publish (3067)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3067) | Release pipeline — manually queued, builds and publishes to PyPI |
Comment thread
RyAuld marked this conversation as resolved.
| [`template-pipeline-stages.yml`](template-pipeline-stages.yml) | — | Shared stages template — PreBuildCheck, Validate, and CI stages reused by both pipelines |
| [`credscan-exclusion.json`](credscan-exclusion.json) | — | CredScan suppression file for known test fixtures |

---

## PR / CI Pipeline (`azure-pipelines.yml`)
## PR / CI Pipeline — [MSAL.Python-PR-OneBranch-Official (3064)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3064)

### Triggers

Expand All @@ -29,13 +29,14 @@ including what each pipeline does, when it runs, and how to trigger a release.
### Stages

```
PreBuildCheck ─► CI
PreBuildCheck ─► CI ─► Benchmark (post-merge to dev only)
```

| Stage | What it does |
|-------|-------------|
| **PreBuildCheck** | Runs SDL security scans: PoliCheck (policy/offensive content), CredScan (leaked credentials), and PostAnalysis (breaks the build on findings) |
| **CI** | Runs the full test suite on Python 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14 |
| Stage | What it does | When it runs |
|-------|-------------|-------------|
| **PreBuildCheck** | Runs SDL security scans: PoliCheck (policy/offensive content), CredScan (leaked credentials), and PostAnalysis (breaks the build on findings) | Always |
| **CI** | Runs the full test suite on Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14 | Always |
| **Benchmark** | Runs performance benchmarks on Python 3.9 and publishes `benchmark-results` artifact | Post-merge pushes to `dev` and manual runs only |
Comment thread
4gust marked this conversation as resolved.

The Validate stage is **skipped** on PR/CI runs (it only applies to release builds).

Expand All @@ -45,7 +46,7 @@ The Validate stage is **skipped** on PR/CI runs (it only applies to release buil

---

## Release Pipeline (`pipeline-publish.yml`)
## Release Pipeline — [MSAL.Python-Publish (3067)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3067)

### Triggers

Expand All @@ -70,7 +71,7 @@ PreBuildCheck ─► Validate ─► CI ─► Build ─┬─► PublishMSALPyt
|-------|-------------|-----------|
| **PreBuildCheck** | PoliCheck + CredScan scans | Always |
| **Validate** | Asserts the `packageVersion` parameter matches `msal/sku.py __version__` | Always (release runs only) |
| **CI** | Full test matrix (Python 3.9–3.14) | After Validate passes |
| **CI** | Full test matrix (Python 3.8–3.14) | After Validate passes |
| **Build** | Builds `sdist` and `wheel` via `python -m build`; publishes `python-dist` artifact | After CI passes |
| **PublishMSALPython** | Uploads to test.pypi.org | `publishTarget == test.pypi.org (Preview / RC)` |
| **PublishPyPI** | Uploads to PyPI via ESRP; requires manual approval | `publishTarget == pypi.org (ESRP Production)` |
Expand Down
6 changes: 3 additions & 3 deletions .Pipelines/pipeline-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ stages:
- stage: Build
displayName: 'Build package'
dependsOn: CI
condition: eq(dependencies.CI.result, 'Succeeded')
condition: in(dependencies.CI.result, 'Succeeded', 'SucceededWithIssues')
Comment thread
4gust marked this conversation as resolved.
Outdated
jobs:
- job: BuildDist
displayName: 'Build sdist + wheel (Python 3.12)'
Expand Down Expand Up @@ -83,7 +83,7 @@ stages:
dependsOn: Build
condition: >
and(
eq(dependencies.Build.result, 'Succeeded'),
in(dependencies.Build.result, 'Succeeded', 'SucceededWithIssues'),
Comment thread
4gust marked this conversation as resolved.
Outdated
eq('${{ parameters.publishTarget }}', 'test.pypi.org (Preview / RC)')
)
jobs:
Expand Down Expand Up @@ -140,7 +140,7 @@ stages:
dependsOn: Build
condition: >
and(
eq(dependencies.Build.result, 'Succeeded'),
in(dependencies.Build.result, 'Succeeded', 'SucceededWithIssues'),
Comment thread
4gust marked this conversation as resolved.
Outdated
eq('${{ parameters.publishTarget }}', 'pypi.org (ESRP Production)')
)
jobs:
Expand Down
32 changes: 31 additions & 1 deletion .Pipelines/template-pipeline-stages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ stages:
GdnBreakGdnToolCredScan: true
GdnBreakGdnToolPoliCheck: true

- task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@3
displayName: 'Publish Security Analysis Logs (TSA)'
condition: succeededOrFailed()
Comment thread
RyAuld marked this conversation as resolved.
inputs:
tsaConfigFile: '$(Build.SourcesDirectory)/.Pipelines/tsaConfig.json'

- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean agent directories'
condition: always()

# ══════════════════════════════════════════════════════════════════════════════
# Stage 1 · Validate — verify packageVersion matches msal/sku.py __version__
# Skipped when runPublish is false (PR / merge builds).
Expand Down Expand Up @@ -127,6 +137,8 @@ stages:
vmImage: ubuntu-latest
strategy:
matrix:
Python38:
python.version: '3.8'
Comment thread
4gust marked this conversation as resolved.
Outdated
Python39:
python.version: '3.9'
Python310:
Expand Down Expand Up @@ -185,11 +197,20 @@ stages:
pip install pytest pytest-azurepipelines
mkdir -p test-results
set -o pipefail
pytest -vv --junitxml=test-results/junit.xml 2>&1 | tee test-results/pytest.log
pytest -vv --benchmark-skip --junitxml=test-results/junit.xml --deselect tests/test_cryptography.py::CryptographyTestCase::test_ceiling_should_be_latest_cryptography_version_plus_three --deselect tests/test_cryptography.py::CryptographyTestCase::test_should_be_run_with_latest_version_of_cryptography 2>&1 | tee test-results/pytest.log
Comment thread
4gust marked this conversation as resolved.
Outdated
displayName: 'Run tests'
retryCountOnTaskFailure: 3
env:
LAB_APP_CLIENT_CERT_PFX_PATH: $(LAB_APP_CLIENT_CERT_PFX_PATH)

# Run cryptography version-gating tests separately as a warning-only check.
# These tests fail when a new cryptography major version is released and setup.cfg
# needs its upper bound bumped. They should not block unrelated PRs.
- bash: |
pytest -vv tests/test_cryptography.py::CryptographyTestCase::test_ceiling_should_be_latest_cryptography_version_plus_three tests/test_cryptography.py::CryptographyTestCase::test_should_be_run_with_latest_version_of_cryptography --junitxml=test-results/junit-crypto-ceiling.xml
displayName: 'Check cryptography ceiling (warning only)'
continueOnError: true

- task: PublishTestResults@2
displayName: 'Publish test results'
condition: succeededOrFailed()
Expand All @@ -199,6 +220,15 @@ stages:
failTaskOnFailedTests: true
testRunTitle: 'Python $(python.version)'

- task: PublishTestResults@2
displayName: 'Publish cryptography ceiling results'
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: 'test-results/junit-crypto-ceiling.xml'
failTaskOnFailedTests: false
testRunTitle: 'Cryptography ceiling check $(python.version)'

- bash: rm -f "$(Agent.TempDirectory)/lab-auth.pfx"
displayName: 'Clean up lab certificate'
condition: always()
17 changes: 17 additions & 0 deletions .Pipelines/tsaConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"codebaseName": "MSAL Python",
"notificationAliases": [
"IdentityDevExDotnet@microsoft.com"
],
"codebaseAdmins": [
"EUROPE\\aadidagt"
],
"instanceUrl": "https://identitydivision.visualstudio.com",
"projectName": "IDDP",
"areaPath": "IDDP\\DevEx-Client-SDK\\Python",
"iterationPath": "IDDP\\Unscheduled",
"tools": [
"credscan",
"policheck"
]
}
Comment thread
RyAuld marked this conversation as resolved.
131 changes: 0 additions & 131 deletions .github/workflows/python-package.yml

This file was deleted.

53 changes: 53 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,56 @@ stages:
- template: .Pipelines/template-pipeline-stages.yml
parameters:
runPublish: false

- stage: Benchmark
displayName: 'Run benchmarks'
dependsOn: CI
# Only run on post-merge pushes to dev — not on PRs or scheduled runs.
# Benchmarks are noisy and the baseline cache is only meaningful on a stable branch.
condition: |
and(
succeeded('CI'),
eq(variables['Build.SourceBranch'], 'refs/heads/dev'),
or(
eq(variables['Build.Reason'], 'IndividualCI'),
eq(variables['Build.Reason'], 'BatchedCI'),
eq(variables['Build.Reason'], 'Manual')
Comment thread
RyAuld marked this conversation as resolved.
)
)
jobs:
- job: Benchmark
displayName: 'Performance benchmarks (Python 3.9)'
pool:
vmImage: ubuntu-latest
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
displayName: 'Set up Python 3.9'

- script: |
python -m pip install --upgrade pip
pip install pytest -r requirements.txt
Comment thread
4gust marked this conversation as resolved.
displayName: 'Install dependencies'

- task: Cache@2
displayName: 'Restore performance baseline cache'
inputs:
key: 'perf-baseline | "$(Agent.OS)" | tests/test_benchmark.py'
path: .perf.baseline

- bash: |
pytest --benchmark-only --benchmark-json benchmark.json --log-cli-level INFO tests/test_benchmark.py
displayName: 'Run benchmarks'

- bash: |
[ -f benchmark.json ] && echo "##vso[task.setvariable variable=benchmarkFileExists]true"
Comment thread
4gust marked this conversation as resolved.
displayName: 'Check benchmark output exists'
condition: succeededOrFailed()

- task: PublishPipelineArtifact@1
displayName: 'Publish benchmark results'
condition: and(succeededOrFailed(), eq(variables['benchmarkFileExists'], 'true'))
inputs:
targetPath: 'benchmark.json'
artifact: 'benchmark-results'
Loading