Repository navigation
Add OneBranch pipeline for PyPI publishing - #8501
Shiva Sathwik Basavapatna Ramaprasad (shivasathwikb) wants to merge 10 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify that CCF-specific ESRP settings are supplied by an authorized Azure DevOps Library variable group. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 1
Open (5)
The wheel is produced in the build job output and then pulled into the release job via the… · Newtomllibis only available in Python 3.11+. If the pipeline image provides an olderpython3,… · New The tag-to-version mapping only replaces the first '-' with '.', which can produce incorrect… · New This assumespython/distcontains only artifacts produced by the current run. If the workspace is… · New Using a fixed venv path under/tmp/ccf-wheel-testcan collide with prior runs on a reused agent,… · New
What changed in this PR
Adds a OneBranch (Azure DevOps) official pipeline to build, validate, and publish the ccf Python wheel to PyPI via ESRP when manually queued from a ccf-* tag.
Changes:
- Introduces a build/validation Bash script that verifies tag/version alignment, checks PyPI for existing versions, builds a single wheel, validates metadata, and smoke-installs/imports it.
- Adds a OneBranch official pipeline YAML with build + ESRP publish stages.
- Documents required Azure DevOps variable group inputs and pipeline behavior.
| File | Description |
|---|---|
| .pipelines/scripts/build-pypi-package.sh | Adds the build + validation logic that runs in the build stage and produces the wheel artifact. |
| .pipelines/pypi.official.yml | Defines the OneBranch build and ESRP-based release stages for publishing to PyPI. |
| .pipelines/README.md | Documents how to run/configure the new OneBranch PyPI release pipeline and prerequisites. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Shiva Sathwik Basavapatna Ramaprasad (@shivasathwikb) Andrea Piccione (@andpiccione) I think this choice is deliberate, and probably right, but I want to take a moment to make sure we are all on the same page: the current release mechanism is that release.yml is triggered on a tag (ccf-*) and produces all release artefacts together (rpm, tgz/npm and wheel/pypi). A Draft GitHub release is created, and all the files are included in it as attachements. A second, manual action (usually preceded by a review) publishes the Github release from its draft state (only visible to repo members) to full release. This then triggers a number of follow-up workflows, including the publication of the tgz/npm to npm (from the GH release attachements) and that of the wheel/pypi to pypi (again from attachements). A consequence of that is that until a release is published, it is in principle possible to cut the tag multiple times, and produce several drafts. This is infrequent, but it has been used in the past, typically when an issue took place in the release workflow that required a change to fix and could not simply be solved by a re-run (the workflow is designed to be idempotent, but the CI infrastructure sometimes changes). With this PR, because the pypi release is trigged purely on tag creation, with no further manual input, we cannot in principle do this, or if we did, we would get the rest of the artefacts built from a different commit than the Python package (I think we can agree that this is undesirable). This may not be big issue, release mishaps are rare, and one way to deal with them would be tombstone the release and skip incomplete attempts rather than retry them. If say, 7.0.23 hit a problem resulting in the Python wheel getting published and not the rpm (or vice versa), we could declare that there was no 7.0.23 and cut a 7.0.24 rather than attempt to fix 7.0.23. There may be other possibilities I have not considered, but I think it is useful that we think about this the same way as we make this change. |
Use existing release validation, isolate build outputs and smoke tests, extract tested wheel metadata validation, and document the production publisher cutover. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Amaury Chamayou (@achamayou) Thanks for calling out the release semantics. The OneBranch pipeline is manual ( |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Release validation and publication gating have unresolved correctness issues.
Review effort: Balanced
Findings: 1
Open (4)
Resolved since last review (5)
The wheel is produced in the build job output and then pulled into the release job via the… Using a fixed venv path under/tmp/ccf-wheel-testcan collide with prior runs on a reused agent,… This assumespython/distcontains only artifacts produced by the current run. If the workspace is… The tag-to-version mapping only replaces the first '-' with '.', which can produce incorrect…tomllibis only available in Python 3.11+. If the pipeline image provides an olderpython3,…
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion' into user/sbasavapatna/onebranch-migration
| esac | ||
|
|
||
| rm -rf python/dist | ||
| "$uv" build --wheel python |
There was a problem hiding this comment.
Do we need to rebuild the python package here if it's already attached to a GitHub release? I'm wondering if it could make more sense to just download the ccf wheel from the latest CCF release and simply upload it to PyPI, instead of rebuilding it from scratch here. The advantage of that approach would be uploading the exact same file that was covered by the release attestation.
There was a problem hiding this comment.
That's a very good point, and if we don't, we need to issue a separate attestation for the pypi package. I think that there are two reasons to go that route: one is to split the release cycle/version numbers of the Python package, as discussed. The other is that I believe the intent in ESRP is to sign things produced in a 1ES environment, rather than produced outside, and so while this may technically work at the moment, it may be blocked eventually.
On the flip side, as you say, pulling it from the GH release would keep a single attestation.


Summary
ccfPython packageccf-*release tagPipeline behavior
The OneBranch pipeline uses:
It is manually queued from a
ccf-*tag after the corresponding GitHub Release has been reviewed and published.The build stage:
ccf-*tagscripts/extract-release-notes.py --target-git-versionto validate the tag, release notes, and package versionpython/distbefore buildingccfwheelccfmodules can be importedThe release stage:
Wheel validation
Wheel metadata validation is implemented in
scripts/validate_python_package.py.The validator verifies that:
.dist-info/METADATAfileccfUnit tests cover:
External prerequisites
/.pipelines/pypi.official.ymlccf-esrp-pypiAzure DevOps Library variable groupOneBranch.Pipelines/GovernedTemplatesThe
ccf-esrp-pypivariable group is expected to provide:ESRP_SERVICE_CONNECTIONESRP_KEY_VAULT_NAMEESRP_SIGN_CERT_NAMEESRP_AUTH_CERT_NAMEESRP_CLIENT_IDESRP_OWNERSESRP_APPROVERSESRP_MAIN_PUBLISHERESRP_DOMAIN_TENANT_IDValidation
bash -nccf-*refs are rejectedgit diff --checkTest result:
Validation limitations
ccf-esrp-pypivariable group and ESRP resources still need to be confirmed or provisionedRollout
The intended release sequence is:
ccf-*release tag.The existing GitHub Actions release and PyPI workflows remain unchanged during non-production validation.
Before enabling OneBranch for production publication, the existing GitHub Actions PyPI publisher must be disabled so that both publishers do not attempt to publish the same version.
If publication is only partially successful, the incomplete version must not be retried by moving or recreating the tag. A new package version should be created instead.