-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add PyPI Trusted Publishing release workflow #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| name: release | ||
|
|
||
| # Builds sdist + wheel and publishes to PyPI on `v*` tags using PyPI Trusted | ||
| # Publishing (OIDC) — no long-lived API token stored in this repo. On pull | ||
| # requests the same build runs as a dry-run: build, `twine check`, install | ||
| # the wheel into a throwaway venv, and import `Wave` from it. That dry-run | ||
| # job never touches PyPI (no `id-token` permission, no publish step). | ||
| # | ||
| # One-time setup before the first `v*` tag: register this repo + workflow as | ||
| # a Trusted Publisher on the `wave-sdk` PyPI project (see AGENTS.md / PR body | ||
| # for the exact fields — this workflow cannot self-register). | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| tags: ["v*"] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: release-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| build: | ||
| name: build + dry-run check | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install build tooling | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install build twine | ||
|
|
||
| - name: Build sdist + wheel | ||
| run: python -m build | ||
|
|
||
| - name: twine check | ||
| run: twine check dist/* | ||
|
|
||
| - name: Create fresh venv (no repo on sys.path) | ||
| run: python -m venv "$RUNNER_TEMP/dry-run" | ||
|
|
||
| - name: Install the built wheel | ||
| run: | | ||
| WHEEL=$(ls dist/*.whl) | ||
| "$RUNNER_TEMP/dry-run/bin/pip" install --upgrade pip | ||
| "$RUNNER_TEMP/dry-run/bin/pip" install "$WHEEL" | ||
|
|
||
| - name: Import check (installed wheel, run away from the repo) | ||
| working-directory: ${{ runner.temp }}/dry-run | ||
| run: | | ||
| bin/python -c " | ||
| from wave_sdk import Wave | ||
| import wave_sdk | ||
| print('wave_sdk', wave_sdk.__version__, 'imported OK from', wave_sdk.__file__) | ||
| print('Wave facade:', Wave) | ||
| " | ||
|
|
||
| - name: Upload dist | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: dist | ||
| path: dist/ | ||
| retention-days: 7 | ||
|
|
||
| publish: | ||
| name: publish to PyPI | ||
| needs: build | ||
| if: startsWith(github.ref, 'refs/tags/v') | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| environment: | ||
| name: pypi | ||
| url: https://pypi.org/project/wave-sdk/ | ||
| permissions: | ||
| # OIDC token for PyPI Trusted Publishing — no API token secret needed. | ||
| id-token: write | ||
| contents: read | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install build tooling | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install build | ||
|
|
||
| - name: Build sdist + wheel | ||
| run: python -m build | ||
|
Comment on lines
+104
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Publish the distribution that passed validation. The 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Verify tag matches package version | ||
| run: | | ||
| TAG_VERSION="${GITHUB_REF_NAME#v}" | ||
| PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") | ||
| if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then | ||
| echo "tag v$TAG_VERSION does not match pyproject.toml version $PKG_VERSION" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Publish to PyPI (Trusted Publishing, OIDC) | ||
| uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 | ||
|
|
||
| - name: Post-publish verification (PyPI + fresh install) | ||
| run: | | ||
| TAG_VERSION="${GITHUB_REF_NAME#v}" | ||
| for i in 1 2 3 4 5 6 7 8; do | ||
| AVAILABLE=$(pip index versions wave-sdk 2>/dev/null | grep -o "$TAG_VERSION" || true) | ||
| if [ -n "$AVAILABLE" ]; then break; fi | ||
| echo "waiting for PyPI to index wave-sdk==$TAG_VERSION (attempt $i)" | ||
| sleep 15 | ||
| done | ||
| python -m venv "$RUNNER_TEMP/verify" | ||
| "$RUNNER_TEMP/verify/bin/pip" install --upgrade pip | ||
| "$RUNNER_TEMP/verify/bin/pip" install "wave-sdk==$TAG_VERSION" | ||
| "$RUNNER_TEMP/verify/bin/python" -c " | ||
| from wave_sdk import Wave | ||
| import wave_sdk | ||
| assert wave_sdk.__version__ == '$TAG_VERSION', wave_sdk.__version__ | ||
| print('verified wave-sdk', wave_sdk.__version__, 'installed from PyPI, Wave facade OK') | ||
| " | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,135p' .github/workflows/release.ymlRepository: wave-av/sdk-python
Length of output: 4739
Other (CWE-829): Inclusion of Functionality from Untrusted Control Sphere
Reachability: External · Exploitability: Difficult
Publish the validated artifact instead of rebuilding in the OIDC job.
The publish job rebuilds
dist/with unpinnedbuildand build-system dependencies, then uploads that output. Download and publish the artifact from thebuildjob. If rebuilding is required, use a hash-locked toolchain.🤖 Prompt for AI Agents