Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/sbom-diff-and-risk-testpypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: sbom-diff-and-risk-testpypi
run-name: sbom-diff-and-risk TestPyPI / ${{ github.event_name }} / ${{ github.ref_name }}

on:
workflow_dispatch:
inputs:
publish_to_testpypi:
description: "Upload to TestPyPI using Trusted Publishing. Requires the TestPyPI publisher to be configured first."
required: true
default: false
type: boolean
push:
paths:
- ".github/workflows/sbom-diff-and-risk-testpypi.yml"
- "tools/sbom-diff-and-risk/PYPI_DESCRIPTION.md"
- "tools/sbom-diff-and-risk/pyproject.toml"
- "tools/sbom-diff-and-risk/src/**"
pull_request:
paths:
- ".github/workflows/sbom-diff-and-risk-testpypi.yml"
- "tools/sbom-diff-and-risk/PYPI_DESCRIPTION.md"
- "tools/sbom-diff-and-risk/pyproject.toml"
- "tools/sbom-diff-and-risk/src/**"

permissions: {}

env:
SBOM_DIFF_RISK_PYTHON_VERSION: "3.11"
SBOM_DIFF_RISK_DIST_ARTIFACT_NAME: sbom-diff-and-risk-testpypi-dist

jobs:
build-and-check:
name: Build and check distributions
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: tools/sbom-diff-and-risk
steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ env.SBOM_DIFF_RISK_PYTHON_VERSION }}

- name: Upgrade pip
run: python -m pip install --upgrade pip

- name: Install package build tooling
run: python -m pip install build twine

- name: Build wheel and source distribution
run: python -m build

- name: Check distribution metadata
run: python -m twine check dist/*

- name: Upload checked distribution artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.SBOM_DIFF_RISK_DIST_ARTIFACT_NAME }}
path: |
tools/sbom-diff-and-risk/dist/*.whl
tools/sbom-diff-and-risk/dist/*.tar.gz
if-no-files-found: error

publish-testpypi:
name: Publish checked distributions to TestPyPI
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_to_testpypi }}
needs: build-and-check
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/project/sbom-diff-and-risk/
permissions:
id-token: write
steps:
- name: Download checked distribution artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.SBOM_DIFF_RISK_DIST_ARTIFACT_NAME }}
path: dist

- name: Publish to TestPyPI with Trusted Publishing
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
packages-dir: dist/
6 changes: 3 additions & 3 deletions tools/sbom-diff-and-risk/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sbom-diff-and-risk

v0.4.1 is a narrow release-only follow-up that validates the repaired tag-path GitHub Release asset publishing flow. It keeps dependency analysis local and deterministic by default and does not change CLI analysis behavior.
v0.5 PR 4 adds a TestPyPI / Trusted Publishing readiness dry-run path. It keeps production PyPI publishing disabled, keeps dependency analysis local and deterministic by default, and does not change CLI analysis behavior.

`sbom-diff-and-risk` is a local, deterministic CLI for comparing two SBOMs or dependency manifests and producing JSON plus Markdown reports.

Expand Down Expand Up @@ -249,7 +249,7 @@ Verification docs:
- [docs/verification.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/verification.md) for the quick decision guide
- [docs/self-provenance.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/self-provenance.md) for workflow-artifact attestation
- [docs/release-provenance.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/release-provenance.md) for release-asset verification and immutable release guidance
- [docs/pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md) for PyPI publishing prerequisites and sequencing
- [docs/pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md) for TestPyPI Trusted Publishing readiness, exact external publisher setup, and production PyPI blockers

## Examples

Expand Down Expand Up @@ -330,7 +330,7 @@ For details on how this repository attests the tool's own wheel and source distr

For details on how version-tag releases publish those same build outputs as release assets, and how consumers can verify immutable releases with GitHub CLI, see [docs/release-provenance.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/release-provenance.md).

For PyPI Trusted Publishing readiness, prerequisites, and the reasons this repository does not enable PyPI upload yet, see [docs/pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md).
For TestPyPI Trusted Publishing readiness, the manually gated dry-run workflow, and the reasons production PyPI upload remains disabled, see [docs/pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md).

## Parser Boundaries

Expand Down
193 changes: 101 additions & 92 deletions tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md
Original file line number Diff line number Diff line change
@@ -1,144 +1,153 @@
# PyPI Trusted Publishing readiness

This page is a readiness checklist, not an enabled publish flow.
This page documents the PR 4 TestPyPI / Trusted Publishing dry-run path for `sbom-diff-and-risk`.

`sbom-diff-and-risk` is not enabling PyPI Trusted Publishing in this PR because the repository is not yet cleanly ready for a narrow, durable publish workflow. The goal here is to make the distribution-authentication story explicit without wiring a half-configured upload path.
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 Publishing overview](https://docs.pypi.org/trusted-publishers/)
- [PyPI Trusted Publishers](https://docs.pypi.org/trusted-publishers/)
- [Adding a Trusted Publisher to an existing PyPI project](https://docs.pypi.org/trusted-publishers/adding-a-publisher/)
- [Creating a PyPI project with a Trusted Publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)
- [Publishing with a Trusted Publisher](https://docs.pypi.org/trusted-publishers/using-a-publisher/)
- [Configuring OpenID Connect in PyPI](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi)
- [PyPA gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish)

## Current status
## PR 4 decision

Today, the repository is ready for:
Use a TestPyPI-first readiness workflow, but do not claim that a TestPyPI dry-run is complete until the external TestPyPI publisher is configured and a maintainer runs the manual upload.

- local package builds with `python -m build`
- package metadata validation with `python -m twine check`
- GitHub workflow artifact attestation for built distributions
- GitHub Release asset publication and release-verification guidance
Current outcome for this PR:

Today, the repository is not yet ready for enabling PyPI Trusted Publishing by default.
- **Trusted Publishing readiness only** by default
- **TestPyPI dry-run blocked by external configuration** until TestPyPI has the matching pending publisher or trusted publisher
- **No production PyPI publishing**

## Why Trusted Publishing is not enabled yet
The workflow file is `.github/workflows/sbom-diff-and-risk-testpypi.yml`.

The main blockers are packaging and release-readiness concerns, not OIDC support itself:
It has two separate jobs:

1. The current `README.md` is repository-oriented and contains local absolute file links such as `D:/OneDrive/...`.
Those links are acceptable for the local Codex app, but they are not a clean PyPI-facing long description.
2. The package does not yet have a dedicated, publish-only GitHub Actions workflow with the minimal Trusted Publishing permissions and a clear separation between build and upload responsibilities.
3. PyPI-side configuration has not been established yet.
That includes either:
- a pending publisher for a new `sbom-diff-and-risk` project, or
- a trusted publisher entry on an existing PyPI project
4. PyPI release sequencing is still intentionally deferred.
The repository now builds version `0.4.1` and has GitHub-hosted release hardening in place, but PyPI publishing is still not enabled in this repository flow.
- `build-and-check` builds the wheel and source distribution, runs `twine check`, and uploads the checked files as a workflow artifact
- `publish-testpypi` downloads that artifact and publishes to TestPyPI with OIDC only when `workflow_dispatch` input `publish_to_testpypi` is set to `true`

Because of those gaps, enabling a publish job now would create a fragile or misleading path.
The upload job does not rebuild the package.

## Local checks that already pass
## Current package and project status

These checks are useful, but they are not sufficient to justify enabling Trusted Publishing:
As checked on April 25, 2026:

```bash
cd tools/sbom-diff-and-risk
python -m build
$files = Get-ChildItem dist | ForEach-Object { $_.FullName }
python -m twine check $files
```
- `https://pypi.org/pypi/sbom-diff-and-risk/json` returned `404`
- `https://test.pypi.org/pypi/sbom-diff-and-risk/json` returned `404`

What those checks prove:
That means neither the production PyPI project nor the TestPyPI project currently exists under `sbom-diff-and-risk`.

- the package can be built locally
- the built distributions pass Twine's metadata/rendering validation
Because the TestPyPI project does not exist yet, the first upload must use a **pending publisher** on TestPyPI, unless a maintainer creates the TestPyPI project some other way first. For production PyPI, defer all configuration and upload work to PR 5.

What they do not prove:
## Workflow identity

- that the README and linked documentation are appropriate for PyPI users
- that the repository and PyPI project are wired together for OIDC publishing
- that GitHub-side and PyPI-side Trusted Publishing configuration matches exactly
Configure the TestPyPI publisher to match this GitHub workflow identity exactly:

## Readiness checklist before enabling Trusted Publishing
| 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` |

Complete these items first:
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.

### 1. Make the package description PyPI-facing
## What this PR validates

- Replace local absolute-path links in `tools/sbom-diff-and-risk/README.md` with links that render sensibly on PyPI.
- If the repository still needs desktop-specific local links, create a separate PyPI-oriented readme or another long-description strategy for packaging.
- Re-run:
Locally and in GitHub Actions, this PR validates:

```bash
cd tools/sbom-diff-and-risk
python -m build
$files = Get-ChildItem dist | ForEach-Object { $_.FullName }
python -m twine check $files
```
- package metadata still points at `PYPI_DESCRIPTION.md` as 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

### 2. Decide the first PyPI-published version and release sequence
This PR does not validate:

- Decide whether the first PyPI upload should be `0.4.1` or a later release.
- Ensure the tag, package version, release notes, GitHub Release assets, and PyPI upload plan all refer to the same version.
- that TestPyPI has the pending publisher configured
- that TestPyPI accepts the first upload
- that production PyPI has a project, pending publisher, or trusted publisher
- that production PyPI publishing should happen for version `0.4.1`

### 3. Configure PyPI-side Trusted Publishing
## TestPyPI setup required before upload

PyPI Trusted Publishing should use OIDC and short-lived credentials instead of a long-lived API token.
Do these steps only after this workflow is merged.

On PyPI, configure either:
1. In GitHub, create or verify the repository environment named `testpypi`.
2. In TestPyPI, create a pending publisher for the new `sbom-diff-and-risk` project.
3. Use the identity values from [Workflow identity](#workflow-identity).
4. Do not add a PyPI API token or GitHub secret for publishing.
5. Run the workflow manually and set `publish_to_testpypi` to `true`.

- a pending publisher for a new project, or
- a trusted publisher for an existing project
If the pending 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.

## Local validation

From `tools/sbom-diff-and-risk`:

```powershell
python -m pip install --upgrade build twine
python -m build
$files = (Get-ChildItem dist -File).FullName
python -m twine check $files
```

Record the exact values that must match GitHub:
What this proves:

- owner: `stacknil`
- repository: `scientific-computing-toolkit`
- workflow file path that will publish
- optional environment name, if the workflow uses one
- the local package can be built
- the built distributions have valid metadata and long-description rendering according to Twine

### 4. Add a dedicated publish workflow only after the above is true
What this does not prove:

When the repository is actually ready, add a dedicated publish workflow that:
- the GitHub OIDC identity matches TestPyPI
- the TestPyPI pending publisher exists
- the upload job can mint a TestPyPI publishing token

- uploads only from previously built distribution files
- uses explicit minimal permissions
- uses OIDC via `id-token: write`
- uses the official PyPA publish action
- does not rebuild the package in the upload step
## GitHub Actions validation

The intended shape is:
Without uploading anything:

- one build job that produces the wheel and sdist
- one publish job that downloads those artifacts and uploads them to PyPI
1. Open **Actions**.
2. Run **sbom-diff-and-risk-testpypi** with `publish_to_testpypi` left as `false`.
3. Confirm `build-and-check` succeeds.
4. Confirm the run uploads `sbom-diff-and-risk-testpypi-dist`.
5. Confirm `publish-testpypi` is skipped.

### 5. Validate on TestPyPI or an equivalent dry-run path first
With TestPyPI pending publisher configured:

Before production PyPI adoption:
1. Open **Actions**.
2. Run **sbom-diff-and-risk-testpypi** with `publish_to_testpypi` set to `true`.
3. Confirm `build-and-check` succeeds before upload.
4. Confirm `publish-testpypi` downloads `sbom-diff-and-risk-testpypi-dist`.
5. Confirm `publish-testpypi` uses OIDC and publishes to `https://test.pypi.org/legacy/`.
6. Open `https://test.pypi.org/project/sbom-diff-and-risk/` and confirm the uploaded version appears.

- validate the workflow against TestPyPI or an equivalent pre-production publisher setup
- confirm the GitHub-side workflow identity exactly matches the PyPI-side trusted publisher configuration
- confirm the upload uses OIDC and no long-lived PyPI token secret
Only after those steps pass can maintainers describe the result as **TestPyPI dry-run completed**.

## What the future Trusted Publishing PR should contain
## Production PyPI boundary

Once the checklist above is complete, the next publishing PR should be narrow and production-oriented:
Production PyPI remains intentionally out of scope for PR 4.

- add a dedicated publish workflow
- document the exact PyPI-side trusted publisher configuration
- document the exact GitHub trigger path for publishing
- preserve the current GitHub workflow artifact attestation and release-asset provenance story
- explain how PyPI distribution provenance relates to, but does not replace, GitHub artifact and release verification
Do not add a production PyPI publish job here. Do not configure production PyPI Trusted Publishing as part of this PR unless it is documented as future preparation only and no upload path is enabled.

## Important boundary
PR 5 should decide:

This repository already has:
- 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

- tool provenance guidance for GitHub workflow artifacts
- release provenance guidance for GitHub Releases and release assets
## Current decision

It does not yet have:
PR 4 stops at a clean readiness state unless a maintainer performs the explicit TestPyPI setup and manual upload after merge.

- enabled PyPI Trusted Publishing
- documented TestPyPI validation
- a production-ready PyPI upload workflow
Until that happens, the correct status is **Trusted Publishing readiness only; TestPyPI upload blocked by external configuration**.
2 changes: 1 addition & 1 deletion tools/sbom-diff-and-risk/docs/release-provenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,4 @@ gh release verify-asset v0.4.0 path/to/sbom_diff_and_risk-0.4.0.tar.gz \
- `gh release verify-asset` verifies a local file path against a release attestation. It does not verify a workflow artifact download directly unless that file is also a release asset.
- GitHub's generated source-code ZIP and tarball downloads are not covered by `gh release verify-asset`.
- A successful release verification does not replace the workflow-artifact attestation story; it complements it.
- This repository still does not add PyPI Trusted Publishing or PyPI provenance in this PR. For prerequisites and sequencing, see [pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md).
- This repository now has a separate TestPyPI Trusted Publishing readiness workflow, but production PyPI publishing remains disabled. For prerequisites, workflow identity, and sequencing, see [pypi-trusted-publishing-readiness.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-trusted-publishing-readiness.md).
Loading
Loading