Skip to content

feat(core): add the PyPI release workflow, and fix two packaging defects - #71

Merged
imran-siddique merged 1 commit into
mainfrom
feat/capture-core-publish
Aug 1, 2026
Merged

feat(core): add the PyPI release workflow, and fix two packaging defects#71
imran-siddique merged 1 commit into
mainfrom
feat/capture-core-publish

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Publishing needs no credentials

I said several times today that publishing needed credentials and was therefore yours. That was wrong in an important way: the house pattern in agent-manifest/.github/workflows/publish.yml already uses Trusted Publishing, where PyPI verifies the workflow's OIDC identity. There is no API token to leak or rotate. This workflow matches that pattern.

Which is also the right answer on the merits: hand-uploading a provenance and integrity package from a laptop with a long-lived token would be the supply-chain practice this project exists to argue against.

Two packaging defects, found by validating the artifact instead of assuming it

The wheel shipped no LICENSE. Apache-2.0 requires the licence travel with the distribution, and pyproject.toml declared the licence as inline text with no file. Now packaged, verified present at dist-info/licenses/LICENSE.

The classifiers claimed Typing :: Typed with no py.typed marker. Consumers got no annotations from a package written entirely with them. The marker ships now, so the claim is true.

Three guards before anything uploads

A PyPI version is claimed forever the moment it lands and cannot be replaced, so the workflow refuses more than it accepts:

Job Refuses
guard a tag that disagrees with the version in pyproject.toml, so a mistyped tag cannot publish a version nobody asked for
consistency vendored copies that differ from the package, which would ship different code on the installed and fallback paths under one version
build smoke test an artifact that does not actually work: installs the wheel into a clean venv, asserts no third-party import came with it, and runs the suite against the installed copy rather than the source tree

Tag-scoped to capture-core-v* rather than a bare python-v*, because this repository holds several packages and a shared pattern would fire this job for an unrelated release.

Verified locally

  • python -m build succeeds; twine check passes on both wheel and sdist.
  • Installs into an empty venv, imports, and pulls in no third-party dependency.
  • LICENSE and py.typed both confirmed inside the wheel.
  • Core suite: 45 passed.

One thing I got wrong, flagged for review

My first draft pinned upload-artifact, download-artifact and action-gh-release to SHAs I wrote from memory. None of those three actions appear in any committed workflow in this repo, so all three were invented and would have pinned to nothing or to something unintended.

Replaced with commits resolved through the GitHub API, and every pin in the file now verifies against it:

actions/checkout               3d3c42e5aac5  OK  2026-07-17
actions/setup-python           5fda3b95a4ea  OK  2026-07-20
actions/upload-artifact        043fb46d1a93  OK  2026-04-10
actions/download-artifact      3e5f45b2cfb9  OK  2026-03-11
pypa/gh-action-pypi-publish    ba38be9e461d  OK  2026-05-21
softprops/action-gh-release    3d0d9888cb7f  OK  2026-07-13

Worth a second look from you regardless, since a fabricated pin in a publish workflow is the worst place for one.

Two human steps left, both on PyPI

  1. Configure a Trusted Publisher on PyPI for repository agentrust-io/integrations, workflow capture-core-publish.yml, environment pypi. The name agentrust-capture-core is currently unclaimed, so this also reserves it.
  2. Push the tag capture-core-v0.1.0.

Nothing else changes on publish: every engine keeps its vendored fallback, so the installed path is an upgrade rather than a dependency.

Publishing needs no credentials. The house pattern in agent-manifest already uses
Trusted Publishing, where PyPI verifies the workflow's OIDC identity, so there is no
API token to leak or rotate. This matches it.

Two defects found while validating the artifact rather than assuming it:

  The wheel shipped no LICENSE. Apache-2.0 requires the licence travel with the
  distribution, and pyproject declared the licence as text with no file. Now
  packaged, verified present at dist-info/licenses/LICENSE.

  The classifiers claimed Typing :: Typed with no py.typed marker, so consumers got
  no annotations from a package written entirely with them. The marker now ships and
  the claim is true.

Tag-scoped to capture-core-v*, not a bare python-v*, because this repository holds
several packages and a shared pattern would fire this job for an unrelated release.

Three guards before anything uploads, because a PyPI version is claimed forever the
moment it lands and cannot be replaced:

  guard        the tag must equal the version in pyproject.toml, so a mistyped tag
               cannot publish a version nobody asked for
  consistency  the vendored copies must match the package, or the installed path and
               the fallback path would ship different code under one version
  smoke test   installs the built wheel into a clean venv, asserts nothing
               third-party came with it, and runs the suite against the installed
               copy rather than the source tree

Verified locally: builds clean, twine check passes on both artifacts, installs into
an empty venv, imports, and pulls in no third-party dependency.

One thing worth flagging in review. My first draft pinned upload-artifact,
download-artifact and action-gh-release to SHAs I wrote from memory. None of those
three actions appear in any committed workflow here, so all three were invented.
Replaced with commits resolved through the GitHub API, and every pin in the file now
verifies against it: checkout, setup-python, upload-artifact, download-artifact,
gh-action-pypi-publish, action-gh-release.

Two human steps remain, both on PyPI rather than here: configure a Trusted Publisher
for this repository, workflow filename capture-core-publish.yml and environment pypi,
then push the tag capture-core-v0.1.0. The name agentrust-capture-core is currently
unclaimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
@imran-siddique
imran-siddique merged commit f6f5b34 into main Aug 1, 2026
11 checks passed
@imran-siddique
imran-siddique deleted the feat/capture-core-publish branch August 1, 2026 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant