Skip to content

ci: add ESRP-based PyPI release pipeline#473

Merged
timenick merged 19 commits into
mainfrom
zhiwang/release-pypi-pipeline
May 25, 2026
Merged

ci: add ESRP-based PyPI release pipeline#473
timenick merged 19 commits into
mainfrom
zhiwang/release-pypi-pipeline

Conversation

@timenick
Copy link
Copy Markdown
Collaborator

@timenick timenick commented May 8, 2026

Summary

Adds .pipelines/modelkit-release-pypi.yml, a OneBranch pipeline that publishes ModelKit wheel + sdist to PyPI via ESRP Release.

ESRP Release is the only Microsoft-compliant path for publishing OSS to PyPI per the ESS Release docs — GitHub Actions and Trusted Publishers are explicitly not approved.

Pipeline shape

Three-job stage Publish, gated to release/* branches:

  1. Prepare (pool.type: windows) — downloads the official build artifacts by OFFICIAL_BUILD_ID, stages only *.whl + *.tar.gz (excludes the *.zip runtime check rules that the official build also emits), sanity-checks wheel filename version against pyproject.toml. Publishes the staged folder as pipeline artifact PyPiPackages via templateContext.outputs.
  2. ManualValidation (pool: Server, agentless) — ManualValidation@0 task with 24h timeout, notifies ESRP_OWNERS, rejects on timeout. Cancellable cheaply if the wrong build was queued.
  3. PublishToESRP (templateContext.type: releaseJob, isProduction: true) — receives PyPiPackages artifact via templateContext.inputs, invokes EsrpRelease@11 with intent: PackageDistribution / ContentType: PyPi / MainPublisher: ESRPRELPACMAN.

Trigger model mirrors modelkit-release-github.yml: trigger: none, manual queue only, OFFICIAL_BUILD_ID parameter required, release/* branch condition on the stage.

Pre-merge requirements (ADO side)

Before the first run, configure these as pipeline variables (or a variable group) in ADO — pipeline references them but does not provide values:

  • ESRP_SERVICE_CONNECTION — ADO service connection onboarded to ESRP Release
  • ESRP_KEYVAULT_NAME — Azure Key Vault holding the TSS signing cert
  • ESRP_SIGN_CERT_NAME — TSS signing cert name (required by task even though PyPI doesn't sign)
  • ESRP_CLIENT_ID — Managed Identity / App Registration client ID authorized for public PyPI
  • ESRP_OWNERS — owner email(s)
  • ESRP_APPROVERS — approver email(s)

DomainTenantId (PME), ServiceEndpointUrl, MainPublisher are hardcoded per the ESS Release docs default.

timenick added 5 commits May 8, 2026 15:11
ESRP Release is the only Microsoft-compliant path for publishing
to PyPI; GitHub Actions / Trusted Publishers are not approved.

Three-job flow (Prepare / ManualValidation / PublishToESRP)
consumes wheel + sdist from official build by OFFICIAL_BUILD_ID
and aligns trigger model with modelkit-release-github.yml
(manual, release/* branch only).
Switch modelkit-release-pypi.yml and modelkit-release-github.yml to extend
v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates, running on the
ProjectReunionESPool-2022 pool with MMS2022-1ES-GPT.

GitHub release: split the original single-job pipeline into Prepare
(downloads official build, stages wheels + parquets) and CreateGitHubRelease
(releaseJob consuming the staged artifact), since 1ES Official only permits
GitHubRelease@1 inside templateContext.type: releaseJob and that job type
cannot run DownloadPipelineArtifact@2. Version is now derived from the
wheel filename instead of pyproject.toml so the release job needs no
source checkout.
…pipeline

# Conflicts:
#	.pipelines/modelkit-release-github.yml
For end-to-end validation only. Revert before going live.
timenick added 2 commits May 14, 2026 14:12
The GitHub release pipeline migration is now its own PR; keep this
branch scoped to the PyPI release pipeline only.
timenick added a commit that referenced this pull request May 14, 2026
)

## Summary

Switches
[.pipelines/modelkit-release-github.yml](.pipelines/modelkit-release-github.yml)
from `v2/OneBranch.Official.CrossPlat.yml@templates` to
`v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates`, running on
`ProjectReunionESPool-2022` with `MMS2022-1ES-GPT`.

This aligns the GitHub release pipeline with the PyPI release pipeline
(#473) on the same 1ES template and pool. Trigger model is unchanged:
`trigger: none`, manual queue only, `OFFICIAL_BUILD_ID` parameter
required, `release/*` branch condition on the stage.

## Pipeline shape

The single-job pipeline is split into two jobs because 1ES Official only
allows `GitHubRelease@1` inside `templateContext.type: releaseJob`, and
a `releaseJob` cannot run `DownloadPipelineArtifact@2`:

1. **Prepare** (`pool.type: windows`) — downloads the official build
artifacts by `OFFICIAL_BUILD_ID`, stages `*.whl` + parquet runtime check
rules into `$(Build.SourcesDirectory)/release_assets`, publishes them as
pipeline artifact `GitHubReleaseAssets` via `templateContext.outputs`.
2. **CreateGitHubRelease** (`templateContext.type: releaseJob`,
`isProduction: true`) — consumes `GitHubReleaseAssets` via
`templateContext.inputs`, derives the version from the wheel filename
(so no source checkout is needed), invokes `GitHubRelease@1` to create
the tag + release.
@timenick timenick marked this pull request as ready for review May 25, 2026 03:40
@timenick timenick requested a review from a team as a code owner May 25, 2026 03:40
@timenick timenick merged commit 63fd3fc into main May 25, 2026
9 checks passed
@timenick timenick deleted the zhiwang/release-pypi-pipeline branch May 25, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants