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
29 changes: 25 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,45 @@ jobs:
if-no-files-found: error
retention-days: 7

# Sigstore bundle per file plus one SLSA provenance for both, keyless via this
# workflow's OIDC identity: docs/releasing.md, "Verifying a release".
attach:
name: Attach to the GitHub Release
name: Sign and attach to the GitHub Release
needs: build
if: github.event_name == 'release'
runs-on: ubuntu-latest
permissions:
contents: write
contents: write # gh release upload
id-token: write # Sigstore keyless signing (OIDC)
attestations: write # build-provenance attestation
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: dist
- name: Upload sdist and wheel
- name: Build provenance
id: provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: dist/*
- name: Sign with Sigstore
uses: sigstore/gh-action-sigstore-python@790bc6befb9d733738f18d8f895854b453640ec9 # v3.5.0
with:
inputs: dist/*
Comment on lines +101 to +107
upload-signing-artifacts: false
release-signing-artifacts: false
- name: Upload sdist, wheel, signatures and provenance
working-directory: .
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.event.release.tag_name }}
run: gh release upload "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --clobber
BUNDLE: ${{ steps.provenance.outputs.bundle-path }}
run: |
# A Sigstore bundle around the in-toto SLSA statement, named .intoto.jsonl
# because that is what provenance checkers (OpenSSF Scorecard) look for.
cp "$BUNDLE" "dist/dokimi-${TAG#v}.intoto.jsonl"
ls -l dist
gh release upload "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --clobber

# Off until a maintainer configures the PyPI Trusted Publisher and sets the
# repository variable PYPI_PUBLISH to true (docs/releasing.md, "Enabling PyPI").
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Added

- **Signed releases with build provenance** — the release workflow signs the
sdist and wheel with Sigstore (keyless, the workflow's identity) and attaches
`<file>.sigstore.json` bundles plus a SLSA build-provenance attestation
(`dokimi-X.Y.Z.intoto.jsonl`, also in `gh attestation verify`) to the GitHub
Release. `docs/releasing.md` shows how to verify a download.
32 changes: 30 additions & 2 deletions dokimi/docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ the `Fragment for package changes` step in `ci.yml`'s `static` job asks for it.
Publishing the release runs `.github/workflows/release.yml`. It checks the tag
against the version, the changelog section and leftover fragments, builds the
sdist and wheel (the wheel from the sdist), smoke-tests the installed wheel
from outside the checkout (`scripts/smoke_installed.py`), attaches both files
to the release and, once enabled, uploads them to PyPI. If it fails, fix on
from outside the checkout (`scripts/smoke_installed.py`), signs both files,
attaches them to the release with their signatures and build provenance
(see [Verifying a release](#verifying-a-release)) and, once enabled, uploads
them to PyPI. If it fails, fix on
`main` and release the next patch version: a published version is never
rebuilt.

Expand All @@ -82,6 +84,32 @@ anything. Given a tag, it checks out that tag (and fails if it does not exist)
and also runs the readiness check on it; without one it builds the ref it was
dispatched from.

## Verifying a release

Each release carries, next to the sdist and wheel:

- `<file>.sigstore.json`: a [Sigstore](https://www.sigstore.dev/) bundle per
file, signed keyless with the identity of `release.yml` on the release tag.
- `dokimi-X.Y.Z.intoto.jsonl`: a SLSA build-provenance attestation for both
files (which workflow run, commit and runner built them), also stored in the
repository's attestations.

```bash
gh release download vX.Y.Z -R intel/dokimi
# signature: signed by release.yml on the tag
python -m pip install sigstore
python -m sigstore verify github dokimi-X.Y.Z-py3-none-any.whl \
--bundle dokimi-X.Y.Z-py3-none-any.whl.sigstore.json \
--repository intel/dokimi --ref refs/tags/vX.Y.Z --name release

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Must-fix. Impact: the documented Sigstore verification rejects genuine release signatures. Trigger: running this command on a signed wheel (the same problem affects the sdist). Evidence: sigstore verify github --name selects the workflow filename in the certificate identity, not the YAML display name; the signer identity ends in .github/workflows/release.yml@refs/tags/vX.Y.Z, whereas --name release expects .github/workflows/release@refs/tags/vX.Y.Z. Workaround: use --name release.yml. Severity: medium because the advertised verification path fails, although signing itself is unaffected.

Suggestion:

Suggested change
--repository intel/dokimi --ref refs/tags/vX.Y.Z --name release
--repository intel/dokimi --ref refs/tags/vX.Y.Z --name release.yml

# provenance, from the attached file (offline) or the repository's attestations
gh attestation verify dokimi-X.Y.Z-py3-none-any.whl -R intel/dokimi \
--signer-workflow intel/dokimi/.github/workflows/release.yml \
--bundle dokimi-X.Y.Z.intoto.jsonl
Comment on lines +98 to +107
```

The same commands check the sdist (`dokimi-X.Y.Z.tar.gz`).
Releases before this (v0.1.0) are unsigned.

## Enabling PyPI

The `pypi` job of the release workflow is off until a maintainer, once:
Expand Down
Loading