This page documents the PR 4 TestPyPI / Trusted Publishing dry-run path for sbom-diff-and-risk.
The PR 5 production PyPI decision gate is documented separately in pypi-production-publishing-decision.md.
The repository now has a safe GitHub Actions path that always builds and checks the Python distributions, and can publish those already-checked distributions to TestPyPI only when a maintainer explicitly enables the manual upload input. It does not publish to production PyPI.
Official references:
- PyPI Trusted Publishers
- Adding a Trusted Publisher to an existing PyPI project
- Creating a PyPI project with a Trusted Publisher
- Publishing with a Trusted Publisher
- Configuring OpenID Connect in PyPI
- PyPA gh-action-pypi-publish
Use a TestPyPI-first readiness workflow, but do not treat TestPyPI success as automatic production PyPI readiness.
Current outcome for this PR:
- Trusted Publishing readiness and TestPyPI dry-run completed after the external TestPyPI publisher was configured and a maintainer manually enabled upload
- No production PyPI publishing
- Production PyPI deferred to the decision gate in pypi-production-publishing-decision.md
The workflow file is .github/workflows/sbom-diff-and-risk-testpypi.yml.
It has two separate jobs:
build-and-checkbuilds the wheel and source distribution, runstwine check, and uploads the checked files as a workflow artifactpublish-testpypidownloads that artifact and publishes to TestPyPI with OIDC only whenworkflow_dispatchinputpublish_to_testpypiis set totrue
The upload job does not rebuild the package.
As checked on April 26, 2026:
https://pypi.org/pypi/sbom-diff-and-risk/jsonreturned404https://test.pypi.org/pypi/sbom-diff-and-risk/jsonreturned200- TestPyPI reports
sbom-diff-and-riskversion0.4.1
That means the production PyPI project is not currently visible under sbom-diff-and-risk, while the TestPyPI dry-run project exists. For production PyPI, use the PR 5 decision gate before adding any production workflow or publisher configuration.
Configure the TestPyPI publisher to match this GitHub workflow identity exactly:
| Field | Value |
|---|---|
| Package/project name | sbom-diff-and-risk |
| GitHub owner | stacknil |
| GitHub repository | scientific-computing-toolkit |
| Workflow file in repository | .github/workflows/sbom-diff-and-risk-testpypi.yml |
| Trusted Publisher workflow name field | sbom-diff-and-risk-testpypi.yml |
| GitHub environment | testpypi |
The workflow uses environment: testpypi for the upload job, so the TestPyPI publisher must also include testpypi as the environment name. If the publisher is configured without an environment, the OIDC identity will not match this workflow.
Locally and in GitHub Actions, this PR validates:
- package metadata still points at
PYPI_DESCRIPTION.mdas the PyPI-facing long description - the package can build a wheel and source distribution
- the built distributions pass
twine check - the GitHub workflow separates build/check from upload
- the TestPyPI upload job uses OIDC-compatible permissions with
id-token: write - no PyPI token secret is required or documented
- production PyPI upload is absent
This PR does not validate:
- that production PyPI has a project, pending publisher, or trusted publisher
- that production PyPI publishing should happen for version
0.4.1 - that a TestPyPI upload is sufficient proof of production PyPI readiness
The completed TestPyPI dry-run required these external steps. Use them again only if the TestPyPI publisher must be recreated.
- In GitHub, create or verify the repository environment named
testpypi. - In TestPyPI, create a pending publisher for the new
sbom-diff-and-riskproject. - Use the identity values from Workflow identity.
- Do not add a PyPI API token or GitHub secret for publishing.
- Run the workflow manually and set
publish_to_testpypitotrue.
If the pending publisher or trusted publisher is missing, or any identity field differs, the upload job should fail instead of silently falling back to a token or pretending the dry-run succeeded.
From tools/sbom-diff-and-risk:
python -m pip install --upgrade build twine
python -m build
$files = (Get-ChildItem dist -File).FullName
python -m twine check $filesWhat this proves:
- the local package can be built
- the built distributions have valid metadata and long-description rendering according to Twine
What this does not prove:
- the GitHub OIDC identity matches TestPyPI
- the TestPyPI pending publisher exists
- the upload job can mint a TestPyPI publishing token
Without uploading anything:
- Open Actions.
- Run sbom-diff-and-risk-testpypi with
publish_to_testpypileft asfalse. - Confirm
build-and-checksucceeds. - Confirm the run uploads
sbom-diff-and-risk-testpypi-dist. - Confirm
publish-testpypiis skipped.
With TestPyPI pending publisher configured:
- Open Actions.
- Run sbom-diff-and-risk-testpypi with
publish_to_testpypiset totrue. - Confirm
build-and-checksucceeds before upload. - Confirm
publish-testpypidownloadssbom-diff-and-risk-testpypi-dist. - Confirm
publish-testpypiuses OIDC and publishes tohttps://test.pypi.org/legacy/. - Open
https://test.pypi.org/project/sbom-diff-and-risk/and confirm the uploaded version appears.
After those steps pass, maintainers can describe the result as TestPyPI dry-run completed.
Production PyPI remains intentionally separate from the TestPyPI dry-run.
Do not add a production PyPI publish job to the TestPyPI workflow. Do not configure production PyPI Trusted Publishing from the TestPyPI readiness process.
PR 5 decides:
- the first production PyPI version
- whether to use a pending publisher or an existing-project trusted publisher
- the production workflow file identity
- the GitHub environment name for production, if any
- how PyPI distribution provenance should be documented alongside GitHub artifact and release verification
See pypi-production-publishing-decision.md for the current production gate.
PR 4 established the TestPyPI readiness workflow and the manual dry-run path. After the external TestPyPI publisher was configured and a maintainer ran the manual upload, the dry-run completed for version 0.4.1.
Production PyPI remains deferred behind the PR 5 gate.