ci: add workflow to publish Python SDK to PyPI on v* tag#638
Conversation
…hon/ change detection
| - name: Checkout (full history) | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
Performance: fetch-depth: 0 fetches the entire repository history including all file blobs. Since this workflow only needs tree objects for git diff (and only builds from sdk/python/), adding filter: 'blob:none' would perform a blobless clone that transfers significantly less data while still satisfying the full-history requirement:
with:
fetch-depth: 0
filter: 'blob:none'|
|
||
| PREV_TAG=$(git tag --list 'v*' \ | ||
| --sort=-creatordate \ | ||
| | grep -v "^${CURRENT_TAG}$" \ |
There was a problem hiding this comment.
Correctness: grep -v "^${CURRENT_TAG}$" interprets the tag value as a regex pattern. Dots in version tags (e.g., v1.0.0) are regex wildcards — "^v1.0.0$" would also match a hypothetical tag like v1X0Y0. Use fixed-string matching instead:
grep -F -x -v "${CURRENT_TAG}"(-F treats the input as a literal string, -x requires a full-line match.)
| - name: Build package | ||
| if: steps.check_changes.outputs.changed == 'true' | ||
| working-directory: sdk/python | ||
| run: python -m build |
There was a problem hiding this comment.
Coverage/Reliability: The SDK has existing tests under sdk/python/tests/ but they are never run before publishing. Consider adding a test step gated on changed == 'true' before the build step:
|
|
||
| - name: Publish to PyPI | ||
| if: steps.check_changes.outputs.changed == 'true' | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| packages-dir: sdk/python/dist/ | ||
| password: ${{ secrets.CUBE_PYPI_TOKEN }} |
There was a problem hiding this comment.
Coverage: After building but before publishing, consider running twine check on the distributions. This validates long_description rendering, classifiers, and metadata — catching common issues before they reach PyPI:
- name: Validate distributions
if: steps.check_changes.outputs.changed == 'true'
working-directory: sdk/python
run: |
pip install twine
twine check dist/*| CURRENT_TAG="${GITHUB_REF_NAME}" | ||
| echo "Current tag: $CURRENT_TAG" | ||
|
|
||
| PREV_TAG=$(git tag --list 'v*' \ |
There was a problem hiding this comment.
Correctness (flagged by multiple reviewers): --sort=-creatordate picks the most recently created tag, not the semantically previous release. A hotfix tag v0.3.1 created after v0.4.0 would be picked as the comparison base instead of v0.4.0, producing a backward diff that silently skips. Prefer DAG-aware resolution:
PREV_TAG=$(git describe --tags --first-parent --match 'v*' --abbrev=0 "${CURRENT_TAG}^" 2>/dev/null || true)…be, tests, twine check)
| if: steps.check_changes.outputs.changed == 'true' | ||
| working-directory: sdk/python | ||
| run: | | ||
| pip install -e ".[dev]" 2>/dev/null || pip install -e . |
There was a problem hiding this comment.
Issue: 2>/dev/null silences real errors from the [dev] extras install.
If the .[dev] install fails (e.g., dependency resolution conflict, network issue, or a syntax error in setup.py), the error is swallowed. The fallback pip install -e . then runs without dev dependencies, so the subsequent pytest call fails with a confusing ModuleNotFoundError rather than surfacing the root cause.
Suggestion: Remove 2>/dev/null and let the install fail loudly. If the intent is defensive fallback, use an explicit diagnostic message:
pip install -e ".[dev]" || { echo "INFO: dev extras not found, installing base"; pip install -e .; }|
|
||
| - name: Publish to PyPI | ||
| if: steps.check_changes.outputs.changed == 'true' | ||
| uses: pypa/gh-action-pypi-publish@release/v1 |
There was a problem hiding this comment.
Issue: Mutable branch reference (@release/v1) for an action that handles a PyPI API token.
Unlike annotated tags (which are conventionally immutable), a branch can receive new commits at any time. If the pypa/gh-action-pypi-publish repo were compromised or a malicious commit force-pushed to release/v1, the change would propagate without review. Because this action handles ${{ secrets.CUBE_PYPI_TOKEN }}, the supply-chain risk is elevated.
Suggestion: Pin to an immutable commit SHA and use Dependabot/Renovate to manage updates with reviewable PRs.
|
|
||
| - name: Check if sdk/python/ changed since last tag | ||
| id: check_changes | ||
| run: | |
There was a problem hiding this comment.
Suggestion: Add set -euo pipefail at the top of this script.
GitHub Actions' default bash shell enables -e but not -u (error on undefined variables) or -o pipefail (propagate errors through pipelines). Adding these guards prevents subtle bugs from misspelled variables or silent pipeline failures.
set -euo pipefail
CURRENT_TAG="${GITHUB_REF_NAME}"
# ... rest unchanged|
I think only officially released tags should trigger automatic PyPI publishing. tags for testing or debugging purposes should not |
Summary
Add a GitHub Actions workflow to automatically publish the Python SDK to PyPI when a
v*tag is pushed.Behavior
v*tag pushessdk/python/has changed since the last tagCUBE_PYPI_TOKENsecretFiles changed
.github/workflows/publish-python-sdk.yml(new)