Skip to content

feat(python): publish sealg to PyPI so uvx sealg resolves - #23

Merged
Miyamura80 merged 1 commit into
mainfrom
claude/publish-sealg-python
Sep 2, 2026
Merged

feat(python): publish sealg to PyPI so uvx sealg resolves#23
Miyamura80 merged 1 commit into
mainfrom
claude/publish-sealg-python

Conversation

@Miyamura80

@Miyamura80 Miyamura80 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Wires a PyPI release path for the Python sealg client (python/) so it is installable by name (uvx sealg) instead of only from a checkout or git subdirectory. The package name sealg is currently free on PyPI. Auth is PyPI Trusted Publishing (OIDC) — no API token is stored.

Changes

  • .github/workflows/publish-python.yaml (new) — builds the sdist + wheel and publishes via pypa/gh-action-pypi-publish using OIDC Trusted Publishing. Triggered on a dedicated sealg-py-vX.Y.Z tag namespace so it never collides with the Rust binary's vX.Y.Z release (release.yml). A tag/version match guard fails the run before publishing if the tag disagrees with python/pyproject.toml. workflow_dispatch runs a build-only validation without publishing.
  • python/pyproject.toml — adds README long-description, license = "MIT" + bundled LICENSE, author, [project.urls], keywords, and classifiers so the PyPI page is complete. Verified the wheel metadata and that uvx --from ./dist/*.whl sealg --help runs.
  • python/LICENSE (new) — copy of the repo MIT license so the wheel bundles it.
  • RELEASING.md — documents the one-time Trusted Publishing setup (maintainer action; CI cannot do it) and the sealg-py-vX.Y.Z release steps.
  • README.md / python/README.md — lead install with uvx sealg; keep the checkout and git-subdirectory fallbacks.

Testing

  • Tests pass (make test) — wire-contract check passes (16 constants agree); package builds sdist + wheel; entrypoint runs from the built wheel.
  • Linting passes (make ci)

Important

Before the first tag can publish, a maintainer must complete the one-time PyPI setup in RELEASING.md: add the trusted publisher (owner Edison-Watch, repo cli, workflow publish-python.yaml, environment pypi) as a pending publisher on PyPI, and create a pypi GitHub Environment. CI cannot do this step.

Related Issues

Closes #

🤖 Generated with Claude Code

https://claude.ai/code/session_01EpwFmgQPfugKFF9zugay6Y


Generated by Claude Code


Summary by cubic

Publishes the Python sealg client to PyPI so uvx sealg resolves without a checkout or git subdirectory.

  • Adds a workflow that builds an sdist and wheel and publishes via PyPI Trusted Publishing (OIDC), with no stored API token.
  • Uses the sealg-py-vX.Y.Z tag namespace so Python releases don't collide with Rust binary vX.Y.Z releases; a tag/version mismatch fails before publishing, and workflow_dispatch does a build-only validation.
  • Fills out pyproject.toml metadata, bundles the MIT license, and updates the READMEs and RELEASING.md.

Migration

  • A maintainer must add the trusted publisher on PyPI and create the pypi GitHub Environment before the first tag can publish; steps are in RELEASING.md.

Written for commit 1b45b44. Summary will update on new commits.

Review in cubic

Wire a PyPI release path for the Python client so it is installable by name
rather than only from a checkout or git subdirectory.

- publish-python.yaml: build sdist+wheel and publish via PyPI Trusted
  Publishing (OIDC, no stored token), triggered on a dedicated `sealg-py-v*`
  tag so it never collides with the Rust binary's `v*` release. A tag/version
  match guard fails before publishing on a mismatch; workflow_dispatch does a
  build-only validation run.
- pyproject.toml: add readme long-description, MIT license + bundled LICENSE,
  authors, project URLs, keywords, and classifiers for a complete PyPI page.
- RELEASING.md: document the one-time Trusted Publishing setup and the
  `sealg-py-vX.Y.Z` release steps.
- READMEs: lead install with `uvx sealg`; keep the checkout/git fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EpwFmgQPfugKFF9zugay6Y
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/publish-python.yaml">

<violation number="1" location=".github/workflows/publish-python.yaml:33">
P3: A manual dispatch on a tag ref would actually publish: workflow_dispatch lets you pick a tag, which sets `github.ref` to `refs/tags/<tag>` and makes this `if` true. That contradicts the documented "build-only validation" purpose of dispatch. Guard on the event name instead, e.g. `if: github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/')`.</violation>

<violation number="2" location=".github/workflows/publish-python.yaml:37">
P3: The version guard compares raw strings, so a pre-release tag like `sealg-py-v0.2.0-rc.1` only passes if python/pyproject.toml spells the version `0.2.0-rc.1` exactly. Both `0.2.0-rc.1` and `0.2.0rc1` are the same PEP 440 version, so the documented pre-release flow fails if a maintainer writes the normalized form. Normalize both sides (e.g. with `packaging.version.Version`) before comparing.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


- name: Verify tag matches package version
# Only enforced on tag pushes; skipped for workflow_dispatch (build-only).
if: startsWith(github.ref, 'refs/tags/')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: A manual dispatch on a tag ref would actually publish: workflow_dispatch lets you pick a tag, which sets github.ref to refs/tags/<tag> and makes this if true. That contradicts the documented "build-only validation" purpose of dispatch. Guard on the event name instead, e.g. if: github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/').

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish-python.yaml, line 33:

<comment>A manual dispatch on a tag ref would actually publish: workflow_dispatch lets you pick a tag, which sets `github.ref` to `refs/tags/<tag>` and makes this `if` true. That contradicts the documented "build-only validation" purpose of dispatch. Guard on the event name instead, e.g. `if: github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/')`.</comment>

<file context>
@@ -0,0 +1,68 @@
+
+      - name: Verify tag matches package version
+        # Only enforced on tag pushes; skipped for workflow_dispatch (build-only).
+        if: startsWith(github.ref, 'refs/tags/')
+        run: |
+          tag_ver="${GITHUB_REF_NAME#sealg-py-v}"
</file context>
Suggested change
if: startsWith(github.ref, 'refs/tags/')
if: github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/')

run: |
tag_ver="${GITHUB_REF_NAME#sealg-py-v}"
pkg_ver="$(python3 -c 'import tomllib,pathlib; print(tomllib.loads(pathlib.Path("python/pyproject.toml").read_text())["project"]["version"])')"
if [ "$tag_ver" != "$pkg_ver" ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The version guard compares raw strings, so a pre-release tag like sealg-py-v0.2.0-rc.1 only passes if python/pyproject.toml spells the version 0.2.0-rc.1 exactly. Both 0.2.0-rc.1 and 0.2.0rc1 are the same PEP 440 version, so the documented pre-release flow fails if a maintainer writes the normalized form. Normalize both sides (e.g. with packaging.version.Version) before comparing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish-python.yaml, line 37:

<comment>The version guard compares raw strings, so a pre-release tag like `sealg-py-v0.2.0-rc.1` only passes if python/pyproject.toml spells the version `0.2.0-rc.1` exactly. Both `0.2.0-rc.1` and `0.2.0rc1` are the same PEP 440 version, so the documented pre-release flow fails if a maintainer writes the normalized form. Normalize both sides (e.g. with `packaging.version.Version`) before comparing.</comment>

<file context>
@@ -0,0 +1,68 @@
+        run: |
+          tag_ver="${GITHUB_REF_NAME#sealg-py-v}"
+          pkg_ver="$(python3 -c 'import tomllib,pathlib; print(tomllib.loads(pathlib.Path("python/pyproject.toml").read_text())["project"]["version"])')"
+          if [ "$tag_ver" != "$pkg_ver" ]; then
+            echo "::error::tag $GITHUB_REF_NAME implies version '$tag_ver' but python/pyproject.toml is '$pkg_ver'. Bump the version before tagging."
+            exit 1
</file context>

@Miyamura80
Miyamura80 merged commit 72f89de into main Sep 2, 2026
8 checks passed
@github-actions
github-actions Bot deleted the claude/publish-sealg-python branch September 2, 2026 20:50
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.

2 participants