Conversation
…tools build env issues
… build e2e skip behaviour
gladjohn
left a comment
There was a problem hiding this comment.
Approved with comments. Please Add Python 3.14 to the matrix in .Pipelines/pipeline-publish.yml
There was a problem hiding this comment.
Pull request overview
Adds a manually triggered Azure DevOps publish pipeline (via .Pipelines/) intended to validate the release version, run the existing test matrix, build sdist/wheel, and publish msal to either test.pypi.org or pypi.org with environment-based approval gating for production releases.
Changes:
- Added a new multi-stage ADO pipeline (
pipeline-publish.yml) composed of reusable step templates for test, build, and publish. - Added documentation (
ADO-PUBLISH-SETUP.md) describing one-time Azure DevOps setup and release walkthrough steps. - Bumped
msalpackage version inmsal/sku.pyto1.35.2rc1.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
msal/sku.py |
Updates the library version used by packaging metadata (setup.cfg reads msal.__version__). |
.Pipelines/pipeline-publish.yml |
Defines Validate → CI → Build → Publish stages and routes publish target via a parameter. |
.Pipelines/template-run-tests.yml |
Installs dependencies and runs pytest, publishing JUnit results. |
.Pipelines/template-build-package.yml |
Builds sdist/wheel, runs twine check, and publishes dist/ as a pipeline artifact. |
.Pipelines/template-publish-package.yml |
Authenticates to a PyPI-compatible feed and uploads built artifacts via twine. |
.Pipelines/template-install-lab-cert.yml |
Retrieves and materializes a lab certificate from Key Vault (currently always invoked by test template). |
.Pipelines/ADO-PUBLISH-SETUP.md |
Documents required ADO service connections/environments and how to run the manual publish pipeline. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…and pipeline diagram
.Pipelines/template-run-tests.yml
Outdated
| # steps: | ||
| # - template: .Pipelines/template-run-tests.yml | ||
|
|
||
| steps: |
There was a problem hiding this comment.
Do we need these templates? Can we have 1 file with all steps? Templates are only useful if reused. I don't think it is the case here.
|
|
||
| ## Overview | ||
|
|
||
| This pipeline is **manually triggered only** — no automatic branch or tag triggers. |
There was a problem hiding this comment.
THerea are 3 sceanrios:
- pipeline which runs on PRs
- pipeline which runs after a merge
- release pipeline
In MSAL.NET I believe these are all 1 pipeline, whith optional config.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.Pipelines/template-run-tests.yml
Outdated
| # Use bash: (not script:) so set -o pipefail works — script: uses /bin/sh on Linux | ||
| # which does not support pipefail; without it, tee always exits 0 masking test failures. |
There was a problem hiding this comment.
The comment claiming that script: uses /bin/sh on Linux (and thus doesn’t support pipefail) is misleading in Azure Pipelines; this repo’s existing azure-pipelines.yml uses script: with set -o pipefail successfully. Either remove/reword the comment to avoid spreading incorrect guidance, or keep the switch to bash: but explain it as making the shell explicit rather than implying script: can’t handle pipefail.
| # Use bash: (not script:) so set -o pipefail works — script: uses /bin/sh on Linux | |
| # which does not support pipefail; without it, tee always exits 0 masking test failures. | |
| # Use bash: here so the shell is explicit and set -o pipefail is reliably honored; | |
| # without pipefail, tee can exit 0 and mask pytest failures. |
.Pipelines/pipeline-publish.yml
Outdated
| SKU_VER=$(grep '__version__' msal/sku.py | sed 's/.*"\(.*\)".*/\1/') | ||
|
|
There was a problem hiding this comment.
The version check parses msal/sku.py with grep/sed, which is brittle (it assumes a specific quoting/format and could break if the assignment line is reformatted). A more robust approach is to have Python load the module and print __version__ (without importing the full package dependency graph), and compare against the parameter.
| SKU_VER=$(grep '__version__' msal/sku.py | sed 's/.*"\(.*\)".*/\1/') | |
| SKU_VER=$(python - << 'PYCODE' | |
| import importlib.util | |
| import pathlib | |
| sku_path = pathlib.Path("msal") / "sku.py" | |
| spec = importlib.util.spec_from_file_location("msal.sku", sku_path) | |
| module = importlib.util.module_from_spec(spec) | |
| assert spec.loader is not None | |
| spec.loader.exec_module(module) | |
| print(module.__version__) | |
| PYCODE | |
| ) |
…; slim pipeline-publish.yml to thin wrapper
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - stage: Build | ||
| displayName: 'Build package' | ||
| dependsOn: CI | ||
| condition: and(eq(dependencies.CI.result, 'Succeeded'), eq('${{ parameters.runPublish }}', 'true')) |
There was a problem hiding this comment.
The Build stage condition compares a boolean template parameter to the string 'true'. Template boolean parameters typically evaluate as booleans (and may render as True/False depending on context), which can cause this condition to evaluate false and skip Build even when runPublish: true. Use a boolean comparison instead (e.g., directly reference the boolean parameter in the condition) to ensure Build runs on publish executions.
| condition: and(eq(dependencies.CI.result, 'Succeeded'), eq('${{ parameters.runPublish }}', 'true')) | |
| condition: and(eq(dependencies.CI.result, 'Succeeded'), eq(${{ parameters.runPublish }}, true)) |
| - script: | | ||
| python -m pip install --upgrade pip | ||
| python -m pip install twine | ||
| displayName: 'Install twine' | ||
|
|
||
| - task: TwineAuthenticate@1 | ||
| displayName: 'Authenticate with MSAL-Test-Python-Upload' | ||
| inputs: | ||
| pythonUploadServiceConnection: MSAL-Test-Python-Upload | ||
|
|
||
| - script: | | ||
| python -m twine upload \ | ||
| -r "MSAL-Test-Python-Upload" \ | ||
| --config-file $(PYPIRC_PATH) \ | ||
| --skip-existing \ | ||
| $(Pipeline.Workspace)/python-dist/* |
There was a problem hiding this comment.
The publish deployment jobs upload from $(Pipeline.Workspace)/python-dist/*, but there is no explicit artifact download step in the Publish stages. In multi-stage YAML (and especially deployment jobs), pipeline artifacts from earlier stages are not reliably available unless you add an explicit download (e.g., download: current / DownloadPipelineArtifact) for the python-dist artifact. Add a download step before the twine upload in both publish stages to ensure the dist files are present.
| - task: AzureKeyVault@2 | ||
| displayName: 'Retrieve lab certificate from Key Vault' | ||
| inputs: | ||
| azureSubscription: 'AuthSdkResourceManager' | ||
| KeyVaultName: 'msidlabs' | ||
| SecretsFilter: 'LabAuth' | ||
| RunAsPreJob: false | ||
|
|
||
| - bash: | | ||
| set -euo pipefail | ||
| CERT_PATH="$(Build.SourcesDirectory)/lab-auth.pfx" | ||
| printf '%s' "$(LabAuth)" | base64 -d > "$CERT_PATH" | ||
| echo "##vso[task.setvariable variable=LAB_APP_CLIENT_CERT_PFX_PATH]$CERT_PATH" | ||
| echo "Lab cert written to: $CERT_PATH ($(wc -c < "$CERT_PATH") bytes)" | ||
| displayName: 'Write lab certificate to disk' |
There was a problem hiding this comment.
These steps run unconditionally in the CI stage, which makes the pipeline dependent on an ADO service connection + Key Vault secret even though the comments say this is 'kept for when e2e tests are fully enabled'. If LabAuth is missing/unavailable (or the service connection isn’t authorized), CI will fail before unit tests run. Make the Key Vault + write-cert steps conditional (e.g., behind a parameter like enableLabAuth / a variable toggle), and only set LAB_APP_CLIENT_CERT_PFX_PATH when the secret is actually retrieved.
| displayName: 'Install NPM' | ||
| inputs: | ||
| versionSpec: '16.x' |
There was a problem hiding this comment.
Pinning Node.js to 16.x is risky because Node 16 is end-of-life and may be removed from hosted agents over time, causing the pre-build stage to break unexpectedly. Consider moving to a currently supported LTS (and update the display name to reflect that this installs Node.js, not NPM).
| displayName: 'Install NPM' | |
| inputs: | |
| versionSpec: '16.x' | |
| displayName: 'Install Node.js' | |
| inputs: | |
| versionSpec: 'lts/*' |
…th); fix CredScan suppression format
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Retrieve the MSID Lab certificate from Key Vault (via AuthSdkResourceManager SC). | ||
| # The cert is written to disk and LAB_APP_CLIENT_CERT_PFX_PATH is set as a variable. | ||
| # Kept for when e2e tests are fully enabled on a lab-capable agent pool. | ||
| # Prerequisites: service connection 'AuthSdkResourceManager' with Get/List access | ||
| # to the 'msidlabs' Key Vault. The 'LabAuth' secret is a base64-encoded PFX. | ||
| - task: AzureKeyVault@2 | ||
| displayName: 'Retrieve lab certificate from Key Vault' | ||
| inputs: | ||
| azureSubscription: 'AuthSdkResourceManager' | ||
| KeyVaultName: 'msidlabs' | ||
| SecretsFilter: 'LabAuth' | ||
| RunAsPreJob: false |
There was a problem hiding this comment.
The CI stage always runs the AzureKeyVault@2 step to fetch the LabAuth secret via the 'AuthSdkResourceManager' service connection. That makes the pipeline depend on a Key Vault + service connection even though LAB_APP_CLIENT_ID is intentionally not set (so e2e tests won’t run). Consider gating the Key Vault fetch + cert write behind an explicit parameter/variable (or only when LAB_APP_CLIENT_ID is provided) so the publish pipeline doesn’t fail due to missing Key Vault wiring.
| - bash: | | ||
| set -euo pipefail | ||
| CERT_PATH="$(Build.SourcesDirectory)/lab-auth.pfx" | ||
| printf '%s' "$(LabAuth)" | base64 -d > "$CERT_PATH" | ||
| echo "##vso[task.setvariable variable=LAB_APP_CLIENT_CERT_PFX_PATH]$CERT_PATH" | ||
| echo "Lab cert written to: $CERT_PATH ($(wc -c < "$CERT_PATH") bytes)" | ||
| displayName: 'Write lab certificate to disk' | ||
|
|
There was a problem hiding this comment.
The lab certificate is written to $(Build.SourcesDirectory)/lab-auth.pfx but never removed. Since it originates from Key Vault, it should be treated as sensitive material; delete it in a final always() step (or write to a temp dir and clean up) to reduce risk of accidental exposure or reuse across steps.
| # Use bash: (not script:) so set -o pipefail works — script: uses /bin/sh on | ||
| # Linux which does not support pipefail; without it, tee always exits 0, | ||
| # masking test failures. |
There was a problem hiding this comment.
The comment claims "script: uses /bin/sh on Linux which does not support pipefail", but this repo’s existing azure-pipelines.yml uses a script: step with set -o pipefail successfully. This comment is likely inaccurate/misleading; please update it (or remove it) to match actual Azure Pipelines behavior.
| # Use bash: (not script:) so set -o pipefail works — script: uses /bin/sh on | |
| # Linux which does not support pipefail; without it, tee always exits 0, | |
| # masking test failures. | |
| # Use set -o pipefail so that pytest failures are not hidden by the pipe to tee. | |
| # Without pipefail, tee exits 0 and the step can succeed even when tests fail. | |
| # This step uses bash: explicitly, but set -o pipefail also works in script: steps. |
| | ADO Project | Under the org; enable **Pipelines** and **Artifacts** | | ||
| | GitHub account with admin rights | Needed to authorize the ADO GitHub App | | ||
| | PyPI API token | Scoped to the `msal` project — generate at <https://pypi.org/manage/account/token/> | | ||
| | MSAL-Python (test.pypi.org) API token | Scoped to the `msal` project on test.pypi.org | |
There was a problem hiding this comment.
This setup guide doesn’t mention the required 'AuthSdkResourceManager' Azure service connection / Key Vault access needed by the CI stage to fetch the LabAuth secret (AzureKeyVault@2 in template-pipeline-stages.yml). Without documenting/setting this up, the pipeline will fail before tests run.
| | MSAL-Python (test.pypi.org) API token | Scoped to the `msal` project on test.pypi.org | | |
| | MSAL-Python (test.pypi.org) API token | Scoped to the `msal` project on test.pypi.org | | |
| | Azure service connection `AuthSdkResourceManager` | Azure Resource Manager service connection with access to the subscription / resource group that contains the Key Vault used by the pipeline. The service principal behind this connection must have at least **Get** permission on **secrets** in that Key Vault. This connection name is referenced by the `AzureKeyVault@2` task in `template-pipeline-stages.yml`. | | |
| | Key Vault secret `LabAuth` | In the Key Vault referenced by `template-pipeline-stages.yml`, create a secret named `LabAuth` containing the lab authentication credentials required by the CI stage. The `AuthSdkResourceManager` service connection must be able to read this secret for the pipeline to succeed. | |
| | Stage | Trigger | Target | | ||
| |-------|---------|--------| | ||
| | **PreBuildCheck** (PoliCheck + CredScan) | always | SDL security scans | | ||
| | **Validate** | release runs only (`runPublish: true`) | asserts `packageVersion` matches `msal/sku.py` | | ||
| | **CI** (tests on Py 3.9–3.14) | after Validate (or immediately on PR/merge runs) | — | | ||
| | **Build** (sdist + wheel) | after CI, release runs only | dist artifact | | ||
| | **PublishMSALPython** | `publishTarget = test.pypi.org (Preview / RC)` | test.pypi.org | | ||
| | **PublishPyPI** | `publishTarget = pypi.org (Production)` | PyPI (production) | |
There was a problem hiding this comment.
The pipeline uses PoliCheck/CredScan/PostAnalysis tasks from the Secure Development Tools extension, but the prerequisites/setup steps don’t call out installing/enabling that extension in the ADO organization/project. Add this requirement so new projects can run PreBuildCheck successfully.
| Codeql.SkipTaskAutoInjection: true | ||
| steps: | ||
| - task: NodeTool@0 | ||
| displayName: 'Install NPM' |
There was a problem hiding this comment.
NodeTool@0 installs Node.js, not NPM specifically; the display name "Install NPM" is misleading. Consider renaming to "Install Node.js" (or clarify why this step is needed for PoliCheck/CredScan).
| displayName: 'Install NPM' | |
| displayName: 'Install Node.js (includes npm)' |
…l comment, Build condition, explicit artifact download, prereq docs
Summary
Adds a manually-triggered ADO release pipeline (
.Pipelines/) that validates, tests, builds, and publishes themsalpackage to test.pypi.org or pypi.org.Following the same template convention as MSAL.NET, all pipeline logic lives in a single shared template (
template-pipeline-stages.yml) called from a thin top-level pipeline wrapper. The template is designed to be reused by future PR-gate and post-merge CI pipelines (withrunPublish: false).Pipeline files
pipeline-publish.ymlrunPublish: truetemplate-pipeline-stages.ymlcredscan-exclusion.jsoncertificate-with-password.pfx,test_mi.py)ADO-PUBLISH-SETUP.mdPipeline stages
credscan-exclusion.json.packageVersionparameter matchesmsal/sku.py __version__(release only)azure-pipelines.ymlbuild)twine check, publishes as pipeline artifact (release only)publishTarget; production stage uses theMSAL-Python-Releaseenvironment for approval gating (release only)Other changes
msal/sku.py— bumped version to1.35.2rc1; fixed typo in module docstring