Skip to content

ci: add workflow to publish Python SDK to PyPI on v* tag#638

Open
wbzdssm wants to merge 2 commits into
TencentCloud:masterfrom
wbzdssm:ci/publish-python-sdk
Open

ci: add workflow to publish Python SDK to PyPI on v* tag#638
wbzdssm wants to merge 2 commits into
TencentCloud:masterfrom
wbzdssm:ci/publish-python-sdk

Conversation

@wbzdssm

@wbzdssm wbzdssm commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a GitHub Actions workflow to automatically publish the Python SDK to PyPI when a v* tag is pushed.

Behavior

  • Triggers on v* tag pushes
  • Checks if sdk/python/ has changed since the last tag
  • If no changes → skips publish (avoids duplicate releases)
  • If changed → builds and publishes to PyPI using CUBE_PYPI_TOKEN secret

Files changed

  • .github/workflows/publish-python-sdk.yml (new)

- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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}$" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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:

Comment on lines +62 to +68

- 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 }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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*' \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)

if: steps.check_changes.outputs.changed == 'true'
working-directory: sdk/python
run: |
pip install -e ".[dev]" 2>/dev/null || pip install -e .

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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: |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

@kinwin-ustc

Copy link
Copy Markdown
Collaborator

I think only officially released tags should trigger automatic PyPI publishing. tags for testing or debugging purposes should not

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