Implement version check for beta indicator in publish workflow and adding release workflow#264
Conversation
NiveditJain
commented
Aug 23, 2025
- Added a step to the GitHub Actions workflow to verify that the version string includes a beta indicator ('b') before allowing publishing to PyPI. This ensures that only versions marked for beta release are published, preventing accidental major releases through the workflow.
- Also adding release workflow to release releases to github
- Added a step to the GitHub Actions workflow to verify that the version string includes a beta indicator ('b') before allowing publishing to PyPI. This ensures that only versions marked for beta release are published, preventing accidental major releases through the workflow.
|
Note Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported. |
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a new GitHub Actions workflow to publish the Python SDK on release.publish events (test + publish jobs with semver normalization and validation) and updates the existing publish workflow to require a beta indicator for its publish path and to use an updated setup-uv action version. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant GH as GitHub Release (event)
participant Runner as Actions Runner
participant Test as Job: test
participant Pub as Job: publish
participant SDK as python-sdk repo
GH->>Runner: release published
Runner->>Test: checkout, setup Python 3.12, setup uv
Test->>SDK: install deps (uv), editable install
Test->>Test: pytest -> coverage & JUnit
Test-->>Runner: upload coverage/artifacts
Runner->>Pub: start (needs: test)
Pub->>SDK: uv sync (--locked --dev)
Pub->>Pub: extract tag -> write exospherehost/_version.py
Pub->>Pub: validate semver (x.y.z) and ensure no 'b'
Pub->>SDK: uv build && uv publish
sequenceDiagram
autonumber
participant CI as Actions Runner
participant Pub as Job: publish (existing)
participant Ver as Version Check
participant SDK as python-sdk repo
CI->>Pub: uv sync
Pub->>SDK: read exospherehost/_version.version
Pub->>Ver: python -c "assert 'b' in exospherehost._version.version"
alt version contains 'b'
Ver-->>Pub: pass -> uv build & publish
else missing 'b'
Ver-->>Pub: fail -> job fails (publish blocked)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/publish-python-sdk.yml (2)
27-29: Align on a single version of setup-uv across jobs.The test job uses
astral-sh/setup-uv@v2while the publish job uses@v6. Prefer one version (v6) everywhere to avoid divergent behavior and caching semantics.- - name: Install uv - uses: astral-sh/setup-uv@v2 + - name: Install uv + uses: astral-sh/setup-uv@v6Also applies to: 73-75
92-95: Optional: Publish provenance (attestations) for PyPI.If your PyPI project has Trusted Publishing enabled, consider enabling provenance for
uv publishfor stronger supply-chain guarantees.Example (if your org permits):
- - run: uv publish + - run: uv publish --provenance
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
.github/workflows/publish-python-sdk-release.yml(1 hunks).github/workflows/publish-python-sdk.yml(1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/publish-python-sdk-release.yml
[warning] 3-3: truthy value should be one of [false, true]
(truthy)
[error] 73-73: trailing spaces
(trailing-spaces)
[error] 80-80: trailing spaces
(trailing-spaces)
[error] 86-86: trailing spaces
(trailing-spaces)
[error] 91-91: trailing spaces
(trailing-spaces)
[error] 97-97: trailing spaces
(trailing-spaces)
[error] 100-100: trailing spaces
(trailing-spaces)
[error] 110-110: trailing spaces
(trailing-spaces)
[error] 115-115: trailing spaces
(trailing-spaces)
[error] 120-120: trailing spaces
(trailing-spaces)
[error] 126-126: no new line character at the end of file
(new-line-at-end-of-file)
[error] 126-126: trailing spaces
(trailing-spaces)
🔇 Additional comments (3)
.github/workflows/publish-python-sdk.yml (2)
40-54: Verify Codecov configuration/secrets.Using
codecov/codecov-action@v5requires a validCODECOV_TOKEN(especially for private repos). Ensure the secret is configured and thatfiles: python-sdk/coverage.xmlmatches the actual output path.You can quickly verify paths and token presence in CI logs; if needed I can script a minimal check, but it depends on your repo secrets visibility.
63-69: Confirm packaging reads version from_version.pyThe workflow’s publish step defaults to
python-sdkand does not bump the version file on main—it will publish whatever is inpython-sdk/exospherehost/_version.pyat commit time. Please verify that your build backend reads the version dynamically from this file—instead of a hard-codedversionin pyproject.toml or setup.py, or inferring from VCS tags—to avoid publishing an incorrect package version.Checklist:
- If you have a
python-sdk/pyproject.toml, ensure there is no static
version = "…"under[project]or[tool.poetry]; or if you rely on a dynamic version plugin, verify it’s configured to read from_version.py.- If you use
setup.py, confirm you import the version in yoursetup()call, for example:from exospherehost._version import version as __version__ setup(..., version=__version__, ...)- To sanity-check locally, run:
rg -nP '^\s*version\s*[:=]' python-sdk/pyproject.toml -C2 || echo 'no static version in pyproject.toml' rg -nP 'version\s*=' python-sdk/exospherehost/_version.py.github/workflows/publish-python-sdk-release.yml (1)
41-50: Codecov config parity with push workflow.Same note as the push workflow: ensure
CODECOV_TOKENis set for this repo and that the pathpython-sdk/coverage.xmlexists in this job context.
| name: Publish Python SDK on Release | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v2 | ||
|
|
||
| - name: Install dev dependencies with uv | ||
| working-directory: python-sdk | ||
| run: | | ||
| uv sync --group dev | ||
|
|
||
| - name: Install python-sdk package (editable) | ||
| working-directory: python-sdk | ||
| run: | | ||
| uv pip install -e . | ||
|
|
||
| - name: Run tests with pytest and coverage | ||
| working-directory: python-sdk | ||
| run: | | ||
| uv run pytest --cov=exospherehost --cov-report=xml --cov-report=term-missing -v --junitxml=pytest-report.xml | ||
|
|
||
| - name: Upload coverage reports to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| slug: exospherehost/exospherehost | ||
| files: python-sdk/coverage.xml | ||
| flags: python-sdk-unittests | ||
| name: python-sdk-coverage-report | ||
| fail_ci_if_error: true | ||
|
|
||
| - name: Upload test results | ||
| uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: python-sdk-test-results | ||
| path: python-sdk/pytest-report.xml | ||
| retention-days: 30 | ||
|
|
||
| publish: | ||
| runs-on: ubuntu-latest | ||
| needs: test | ||
| defaults: | ||
| run: | ||
| working-directory: python-sdk | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: astral-sh/setup-uv@v6 | ||
| name: Install uv | ||
|
|
||
| - run: uv python install | ||
|
|
||
| - run: uv sync --locked --dev | ||
|
|
||
| - name: Update version to release tag | ||
| run: | | ||
| # Extract version from tag (remove 'v' or 'V' prefix if present) | ||
| RELEASE_VERSION="${{ github.ref_name }}" | ||
|
|
||
| # Handle empty or invalid tags | ||
| if [[ -z "$RELEASE_VERSION" ]]; then | ||
| echo "ERROR: Release tag is empty" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Remove 'v' or 'V' prefix (case insensitive) | ||
| if [[ $RELEASE_VERSION =~ ^[vV] ]]; then | ||
| RELEASE_VERSION="${RELEASE_VERSION#?}" # Remove first character | ||
| fi | ||
|
|
||
| # Validate the extracted version is not empty | ||
| if [[ -z "$RELEASE_VERSION" ]]; then | ||
| echo "ERROR: Invalid tag format - version part is empty" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Update the version file | ||
| echo "version = \"$RELEASE_VERSION\"" > exospherehost/_version.py | ||
|
|
||
| echo "Updated version to $RELEASE_VERSION for release" | ||
|
|
||
| - name: Verify version format | ||
| run: | | ||
| python -c " | ||
| import sys | ||
| sys.path.append('.') | ||
| from exospherehost._version import version | ||
| import re | ||
|
|
||
| # Check if version matches semantic versioning pattern | ||
| if not re.match(r'^\d+\.\d+\.\d+$', version): | ||
| print(f'ERROR: Version {version} does not match semantic versioning pattern (x.y.z)') | ||
| sys.exit(1) | ||
|
|
||
| # Ensure it's not a beta version (should not contain 'b') | ||
| if 'b' in version: | ||
| print(f'ERROR: Release version {version} contains beta indicator. Release versions should not be beta.') | ||
| sys.exit(1) | ||
|
|
||
| print(f'Version {version} is valid for release publishing') | ||
| " | ||
|
|
||
| - run: uv build | ||
|
|
||
| - run: uv publish No newline at end of file |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
General: consider provenance on PyPI and SBOMs for releases.
For release artifacts, enabling provenance (uv publish --provenance) and attaching an SBOM (e.g., pip install pip-audit && pip-audit -r ... or uv export --format=requirements-txt | cyclonedx-py) improves supply-chain posture.
🧰 Tools
🪛 YAMLlint (1.37.1)
[warning] 3-3: truthy value should be one of [false, true]
(truthy)
[error] 73-73: trailing spaces
(trailing-spaces)
[error] 80-80: trailing spaces
(trailing-spaces)
[error] 86-86: trailing spaces
(trailing-spaces)
[error] 91-91: trailing spaces
(trailing-spaces)
[error] 97-97: trailing spaces
(trailing-spaces)
[error] 100-100: trailing spaces
(trailing-spaces)
[error] 110-110: trailing spaces
(trailing-spaces)
[error] 115-115: trailing spaces
(trailing-spaces)
[error] 120-120: trailing spaces
(trailing-spaces)
[error] 126-126: no new line character at the end of file
(new-line-at-end-of-file)
[error] 126-126: trailing spaces
(trailing-spaces)
🤖 Prompt for AI Agents
.github/workflows/publish-python-sdk-release.yml lines 1-126: the publish job
doesn’t produce or attach provenance/SBOM artifacts; update the publish steps to
(1) run uv publish with the --provenance flag to upload provenance to PyPI, (2)
generate an SBOM (for example use uv export --format=requirements-txt >
requirements.txt then run cyclonedx-bom or pip-audit to produce an SBOM file),
and (3) upload the resulting provenance and SBOM files as workflow artifacts (or
attach them to the release) so they are retained with the release.
There was a problem hiding this comment.
@NiveditJain Great question! Let me explain provenance and SBOMs in the context of software supply chain security:
Provenance
Provenance is cryptographic proof of how your software package was built. When you use uv publish --provenance, it:
- Records the build environment: Which GitHub Actions runner, commit SHA, workflow file, etc.
- Creates cryptographic signatures: Uses GitHub's OIDC tokens to prove the package came from your specific repo/workflow
- Uploads to PyPI: The provenance data gets attached to your package on PyPI
- Enables verification: Users can verify your package wasn't tampered with and actually came from your GitHub repo
SBOM (Software Bill of Materials)
An SBOM is like an "ingredient list" for your software package:
- Lists all dependencies: Every library, version, and transitive dependency
- Tracks vulnerabilities: Security scanners can check SBOMs for known CVEs
- Compliance requirement: Many organizations now require SBOMs for software they use
- Supply chain visibility: Helps identify what's actually in your package
Why This Matters for Your SDK
- Trust: Users can verify your packages are legitimate
- Security: Easier to detect supply chain attacks
- Compliance: Many enterprises require provenance/SBOMs
- Best practice: Industry standard for mature software projects
Implementation
For your workflow, it would look like:
- run: uv publish --provenance # Adds cryptographic proof
- name: Generate SBOM
run: |
uv export --format=requirements-txt > requirements.txt
cyclonedx-py -r requirements.txt -o sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.jsonThink of it as adding a "digital passport" and "ingredient list" to your software packages!
There was a problem hiding this comment.
@coderabbitai lets add this in next cycle create an issue for this please!
There was a problem hiding this comment.
🧩 Analysis chain
I'll create a GitHub issue to track this enhancement for the next development cycle!
@NiveditJain Perfect! I've created a GitHub issue to track the implementation of provenance and SBOM support for your Python SDK release workflow. The issue includes:
- Clear technical requirements for both provenance and SBOM implementation
- Benefits explanation covering security, compliance, and best practices
- Acceptance criteria to guide the implementation
- Proper backlinks to this PR and our discussion
- Assignment to you as requested
This enhancement will significantly improve your SDK's supply chain security posture when you're ready to implement it in the next development cycle! 🚀
/| /|
( :v: :v: )
|(_)|
| | *thinking about security improvements*
/ \
🏁 Script executed:
#!/bin/bash
# Create a GitHub issue for implementing provenance and SBOM support
gh issue create \
--title "Add provenance and SBOM support to Python SDK release workflow" \
--body "## Overview
Enhance the Python SDK release workflow to include supply chain security features: provenance and Software Bill of Materials (SBOM) generation.
## Background
During review of PR #264, it was identified that the release workflow should include:
1. **Provenance**: Cryptographic proof of how the package was built using GitHub's OIDC tokens
2. **SBOM**: Software Bill of Materials listing all dependencies for security scanning and compliance
## Implementation Requirements
### Provenance
- Add \`--provenance\` flag to \`uv publish\` command
- This will automatically generate and upload cryptographic signatures to PyPI
- Enables users to verify packages weren't tampered with and came from the official repo
### SBOM Generation
- Generate requirements file: \`uv export --format=requirements-txt > requirements.txt\`
- Create SBOM using tools like \`cyclonedx-py\` or \`pip-audit\`
- Upload SBOM as workflow artifact and/or attach to GitHub release
### Benefits
- **Enhanced Security**: Users can verify package authenticity
- **Vulnerability Tracking**: SBOMs enable automated security scanning
- **Compliance**: Meet enterprise requirements for software supply chain transparency
- **Industry Best Practice**: Aligns with modern software security standards
## Acceptance Criteria
- [ ] \`uv publish --provenance\` flag added to release workflow
- [ ] SBOM generation integrated into workflow
- [ ] SBOM uploaded as workflow artifact
- [ ] Documentation updated to explain provenance/SBOM benefits
- [ ] Verify provenance appears correctly on PyPI
## References
- Original discussion: https://github.com/exospherehost/exospherehost/pull/264#discussion_r2296060830
- Related PR: https://github.com/exospherehost/exospherehost/pull/264
- Requested by: @NiveditJain" \
--assignee "NiveditJain"
Length of output: 1958
@NiveditJain Perfect! ✅ I've successfully created GitHub issue #265 to track the implementation of provenance and SBOM support for your Python SDK release workflow.
The issue is now:
- Created and assigned to you for the next development cycle
- Properly documented with technical requirements and benefits
- Linked back to this PR and our discussion for full context
- Ready for implementation when you're ready to tackle supply chain security enhancements
You can track progress on the issue and reference it in future PRs when implementing these security improvements! 🎯
/| /|
( :v: :v: )
|(_)|
| ✓ | *mission accomplished*
/ \
- Changed the setup-uv action from version v2 to v6 in the publish-python-sdk.yml workflow file to ensure compatibility with the latest features and improvements.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.github/workflows/publish-python-sdk.yml (3)
76-79: Publish job’s Python version is implicit; make it explicit to avoid wheel/runtime mismatches.Without specifying the version,
uv python installmay select an environment different from CI tests. Align with 3.12 used in the test job.- - run: uv python install + - run: uv python install 3.12 - run: uv sync --locked --dev
92-95: Avoid double-building artifacts.
uv publishbuilds by default; either drop the explicituv buildor pass--no-buildtouv publish.Option A:
- - run: uv build - - run: uv publish + - run: uv publishOption B:
- run: uv build - - run: uv publish + - run: uv publish --no-build
35-44: Optional: fail fast by running the beta gate during the test job too.Catching non-beta versions earlier shortens feedback loops. Add the same check step in the test job (working directory set to python-sdk).
Example insertion after “Install dev dependencies with uv”:
- name: Check version for beta indicator working-directory: python-sdk run: | python - <<'PY' import re, pathlib, sys m = re.search(r'^version\s*=\s*[\'"](.+?)[\'"]\s*$', pathlib.Path("exospherehost/_version.py").read_text(encoding="utf-8"), re.M) if not m or not re.fullmatch(r'\d+\.\d+\.\d+b\d+', m.group(1)): print("ERROR: Version is not a beta (expected X.Y.ZbN)"); sys.exit(1) print("Version is beta; continuing.") PY
♻️ Duplicate comments (1)
.github/workflows/publish-python-sdk.yml (1)
80-90: Fix: current beta check can fail with IndentationError and is too permissive; avoid importing the package and validate PEP 440 beta strictly.
- Using
python -c "...\n import sys\n"with leading indentation will raiseIndentationErrorat runtime in CI.- Importing
exospherehostat build time can execute top-level code and require deps; brittle.'b' in versionis too loose; enforce a PEP 440 beta likeX.Y.ZbN.Replace the step with a heredoc that parses the version file and validates strictly.
- - name: Check version for beta indicator - run: | - python -c " - import sys - sys.path.append('.') - from exospherehost._version import version - if 'b' not in version: - print(f'ERROR: Version {version} does not contain beta indicator (b). Major releases are only allowed through GitHub releases.') - sys.exit(1) - print(f'Version {version} is valid for PyPI publishing (contains beta indicator)') - " + - name: Check version for beta indicator + run: | + python - <<'PY' + import re, pathlib, sys + ver_file = pathlib.Path("exospherehost/_version.py") + try: + text = ver_file.read_text(encoding="utf-8") + except FileNotFoundError: + print("ERROR: exospherehost/_version.py not found") + sys.exit(1) + m = re.search(r'^version\s*=\s*[\'"](.+?)[\'"]\s*$', text, re.M) + if not m: + print("ERROR: Could not find version in exospherehost/_version.py") + sys.exit(1) + version = m.group(1) + # Require PEP 440 beta: e.g., 1.2.3b1 + if not re.fullmatch(r'\d+\.\d+\.\d+b\d+', version): + print(f"ERROR: Version {version} is not a beta (expected X.Y.ZbN). Major releases are only allowed via GitHub releases.") + sys.exit(1) + print(f"Version {version} is valid for PyPI publishing (beta).") + PY
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.github/workflows/publish-python-sdk.yml(2 hunks)
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v2 | ||
| with: | ||
| cache: true | ||
| uses: astral-sh/setup-uv@v6 | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Good upgrade to setup-uv v6; consider pinning to a commit SHA for supply-chain hardening.
Pinning actions to a specific commit digest prevents unexpected changes when the action’s tag is moved.
Example:
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@<commit-sha>
# v6 ref: https://github.com/astral-sh/setup-uv/commits/v6🤖 Prompt for AI Agents
.github/workflows/publish-python-sdk.yml lines 27-29: the workflow currently
references the action with a moving tag ("astral-sh/setup-uv@v6"); to harden
supply chain, replace the tag with the specific commit SHA for the v6 release.
Locate the v6 tag commit SHA from the action repo (e.g., the v6 tag commit on
GitHub), update the uses entry to "astral-sh/setup-uv@<commit-sha>" and
optionally add a comment referencing the v6 tag URL for future maintainers.