|
| 1 | +# Production PyPI publishing decision |
| 2 | + |
| 3 | +This page records the PR 5 production PyPI gate for `sbom-diff-and-risk`. |
| 4 | + |
| 5 | +## PR 5 decision |
| 6 | + |
| 7 | +Production PyPI publishing is **deferred, but conditionally allowed after the prerequisites below are complete**. In short: production PyPI is currently deferred. |
| 8 | + |
| 9 | +PR 5 does not add an enabled production publishing workflow and does not publish to production PyPI. The successful TestPyPI Trusted Publishing dry-run proves that the package metadata can render on TestPyPI and that the TestPyPI OIDC path can work, but it is not automatic proof that production PyPI publishing is ready. |
| 10 | + |
| 11 | +The production gate is intentionally conservative because: |
| 12 | + |
| 13 | +- the production PyPI project does not currently exist under the intended name |
| 14 | +- the package metadata still declares version `0.4.1` |
| 15 | +- the first production upload should be a deliberate release version, not an old dry-run version |
| 16 | +- the production PyPI pending publisher or trusted publisher has not been configured |
| 17 | +- the production GitHub environment has not yet been confirmed |
| 18 | + |
| 19 | +## Package name and external state |
| 20 | + |
| 21 | +The production package name should be `sbom-diff-and-risk`. |
| 22 | + |
| 23 | +As checked on April 26, 2026: |
| 24 | + |
| 25 | +- `https://pypi.org/pypi/sbom-diff-and-risk/json` returned `404` |
| 26 | +- `https://test.pypi.org/pypi/sbom-diff-and-risk/json` returned `200` |
| 27 | +- TestPyPI reports `sbom-diff-and-risk` version `0.4.1` |
| 28 | + |
| 29 | +This means the intended production project name is not currently visible on production PyPI, while the TestPyPI dry-run project exists. Treat the production name as available for this decision, but re-check immediately before configuration because PyPI can reserve, prohibit, or receive new projects at any time. The first production upload should use a production PyPI pending publisher unless the project is created by a maintainer before the publishing workflow is enabled. |
| 30 | + |
| 31 | +## First production version |
| 32 | + |
| 33 | +Do not publish `0.4.1` to production PyPI casually. |
| 34 | + |
| 35 | +The first production PyPI version should be `0.5.0` only if v0.5 is approved as the first production package release. Otherwise, defer to a later GitHub release tag. |
| 36 | + |
| 37 | +For the first production upload: |
| 38 | + |
| 39 | +- the GitHub tag should be `v<version>` |
| 40 | +- `tools/sbom-diff-and-risk/pyproject.toml` should declare the matching `<version>` |
| 41 | +- the GitHub release and release assets should be available for the same tag |
| 42 | +- the production PyPI workflow should run from the matching tag ref |
| 43 | +- the production PyPI upload should use the checked distributions from that workflow run |
| 44 | + |
| 45 | +## Production publisher identity |
| 46 | + |
| 47 | +Configure the production PyPI publisher to match this identity exactly: |
| 48 | + |
| 49 | +| Field | Value | |
| 50 | +| --- | --- | |
| 51 | +| PyPI project name | `sbom-diff-and-risk` | |
| 52 | +| GitHub owner | `stacknil` | |
| 53 | +| GitHub repository | `scientific-computing-toolkit` | |
| 54 | +| Future workflow file path | `.github/workflows/sbom-diff-and-risk-pypi.yml` | |
| 55 | +| Trusted Publisher workflow name field | `sbom-diff-and-risk-pypi.yml` | |
| 56 | +| GitHub environment | `pypi` | |
| 57 | + |
| 58 | +If production PyPI still has no project for this name, configure a pending publisher for a new project. If the project exists by the time production publishing is implemented, add the trusted publisher to the existing project instead. |
| 59 | + |
| 60 | +Do not create or document a PyPI API token for this workflow. Production upload should use Trusted Publishing / OIDC only. |
| 61 | + |
| 62 | +PyPI-side setup should use these paths: |
| 63 | + |
| 64 | +- for a new production project, create a pending publisher on production PyPI for project `sbom-diff-and-risk` with the owner, repository, workflow, and environment values above |
| 65 | +- for an existing production project, open that project on production PyPI and add a trusted publisher with the same owner, repository, workflow, and environment values |
| 66 | +- leave the environment field as `pypi`; if the PyPI publisher omits the environment, it will not match the future publish job identity |
| 67 | +- do not add a PyPI API token, PyPI password, or GitHub publishing secret as a fallback |
| 68 | + |
| 69 | +## Prerequisites before enabling production publishing |
| 70 | + |
| 71 | +Before adding `.github/workflows/sbom-diff-and-risk-pypi.yml`, maintainers should complete all of these checks: |
| 72 | + |
| 73 | +- confirm the intended production package name still resolves as expected on production PyPI |
| 74 | +- choose the first production version, likely `0.5.0` or a later release tag |
| 75 | +- update `pyproject.toml` to that version |
| 76 | +- create or verify the matching GitHub tag and release assets |
| 77 | +- create the GitHub environment named `pypi` |
| 78 | +- configure required reviewers or equivalent repository controls on the `pypi` environment |
| 79 | +- create the PyPI pending publisher or existing-project trusted publisher with the exact identity above |
| 80 | +- run the future workflow in no-publish mode first and confirm the publish job is skipped |
| 81 | +- verify the checked distributions with `python -m twine check dist/*` |
| 82 | + |
| 83 | +## Future workflow shape |
| 84 | + |
| 85 | +PR 5 intentionally documents the future workflow shape without enabling it. |
| 86 | + |
| 87 | +The future production workflow should: |
| 88 | + |
| 89 | +- use `workflow_dispatch` only for the initial production publishing process |
| 90 | +- require an explicit boolean input such as `publish_to_pypi` |
| 91 | +- require a confirmation string such as `publish sbom-diff-and-risk to production PyPI` |
| 92 | +- require an expected version input and assert that it matches `pyproject.toml` |
| 93 | +- require the run ref to be a version tag such as `refs/tags/v0.5.0` |
| 94 | +- build the wheel and source distribution once |
| 95 | +- run `python -m twine check dist/*` |
| 96 | +- upload the checked distributions as a workflow artifact |
| 97 | +- publish only from a separate gated job that downloads that artifact |
| 98 | +- use the GitHub environment `pypi` on the publish job |
| 99 | +- grant `id-token: write` only to the publish job |
| 100 | +- avoid production upload on ordinary push or pull request events |
| 101 | + |
| 102 | +The publish step should use `pypa/gh-action-pypi-publish@release/v1` without a `repository-url` override so it targets production PyPI. |
| 103 | + |
| 104 | +## Provenance boundaries |
| 105 | + |
| 106 | +Production PyPI Trusted Publishing provenance, GitHub workflow artifact attestations, and GitHub Release asset verification answer related but different questions. |
| 107 | + |
| 108 | +PyPI Trusted Publishing provenance answers: |
| 109 | + |
| 110 | +- was this distribution uploaded to this PyPI project through the configured GitHub publisher identity? |
| 111 | +- did the upload use the expected owner, repository, workflow file, and environment? |
| 112 | + |
| 113 | +GitHub workflow artifact attestations answer: |
| 114 | + |
| 115 | +- were these local wheel or source distribution bytes built by `.github/workflows/sbom-diff-and-risk-ci.yml`? |
| 116 | +- do the downloaded files match the attested workflow subjects? |
| 117 | + |
| 118 | +GitHub Release verification answers: |
| 119 | + |
| 120 | +- does the GitHub Release record have a valid release attestation? |
| 121 | +- does a downloaded release asset match an attested asset from an immutable release? |
| 122 | + |
| 123 | +Do not treat one provenance surface as a replacement for the others. A PyPI package can have valid Trusted Publishing provenance without proving that it is byte-for-byte identical to a GitHub Release asset. Consumers who need cross-surface verification should compare hashes between a PyPI download and the corresponding GitHub Release asset, then use the GitHub verification flows documented in `self-provenance.md` and `release-provenance.md`. |
| 124 | + |
| 125 | +## Consumer guidance after a future production release |
| 126 | + |
| 127 | +After production publishing is enabled in a later PR, consumers should: |
| 128 | + |
| 129 | +- install only the intended project name, `sbom-diff-and-risk` |
| 130 | +- check that the PyPI version matches the expected GitHub release tag |
| 131 | +- inspect PyPI's Trusted Publishing provenance for the expected publisher identity |
| 132 | +- use GitHub artifact attestation verification for workflow-built files when downloading from GitHub |
| 133 | +- use GitHub Release verification for immutable release assets when relying on GitHub Releases |
| 134 | +- continue to treat TestPyPI as a dry-run environment only |
| 135 | + |
| 136 | +## PR 5 local verification |
| 137 | + |
| 138 | +PR 5 should remain a documentation and gate-design change. Local verification is still required to prove the existing package surface did not regress: |
| 139 | + |
| 140 | +```powershell |
| 141 | +python -m build |
| 142 | +python -m twine check dist/* |
| 143 | +python -m pytest |
| 144 | +git diff --check |
| 145 | +``` |
0 commit comments