Skip to content

SDK regeneration

SDK regeneration #342

Workflow file for this run

name: Release
on:
push:
branches: [main]
paths: ["pyproject.toml"]
pull_request:
workflow_dispatch:
inputs:
dry_run:
description: "Build and check the distribution without publishing"
type: boolean
default: true
permissions:
contents: read
concurrency:
group: publish-whop-sdk
cancel-in-progress: false
env:
PYPI_PROJECT: whop-sdk
PYTHON_VERSION: "3.12"
jobs:
decide:
runs-on: ubuntu-latest
outputs:
release: ${{ steps.gate.outputs.release }}
dry: ${{ steps.gate.outputs.dry }}
version: ${{ steps.gate.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install version tooling
run: python -m pip install --disable-pip-version-check --quiet packaging
- name: Decide whether to release
id: gate
env:
EVENT: ${{ github.event_name }}
DRY_RUN: ${{ github.event.inputs.dry_run }}
run: |
set -euo pipefail
emit() { echo "$1=$2" >> "$GITHUB_OUTPUT"; }
version=$(python - <<'PY'
import tomllib
with open("pyproject.toml", "rb") as handle:
config = tomllib.load(handle)
print(config.get("tool", {}).get("poetry", {}).get("version") or "")
PY
)
if [ -z "$version" ]; then
echo "::notice::pyproject.toml has no [tool.poetry] version — this is not the Fern-generated tree, so there is nothing to release."
emit release false
emit dry true
emit version ""
exit 0
fi
echo "pyproject.toml version: $version"
emit version "$version"
if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.]+)?$'; then
echo "::error::'$version' is not a version this workflow will publish."
exit 1
fi
if [ "$EVENT" = "pull_request" ] || [ "$DRY_RUN" = "true" ]; then
echo "Dry run: building and checking the distribution, publishing nothing."
emit release true
emit dry true
exit 0
fi
emit dry false
if [ "$EVENT" = "push" ]; then
changed=$(git diff --name-only HEAD^ HEAD)
if [ "$changed" != "pyproject.toml" ]; then
echo "::error::a release push must change pyproject.toml and nothing else. Changed:"
printf '%s\n' "$changed"
exit 1
fi
body=$(git diff -U0 HEAD^ HEAD -- pyproject.toml | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' || true)
if [ "$(printf '%s\n' "$body" | grep -cvE '^[+-]version = ')" -ne 0 ]; then
echo "::error::a release push must change only the version line. Diff:"
printf '%s\n' "$body"
exit 1
fi
fi
status=$(curl -sS -o /dev/null -w '%{http_code}' \
"https://pypi.org/pypi/${PYPI_PROJECT}/${version}/json")
case "$status" in
200)
echo "::notice::${PYPI_PROJECT} ${version} is already on PyPI; nothing to publish."
emit release false
exit 0
;;
404) ;;
*)
echo "::error::PyPI returned HTTP $status for ${PYPI_PROJECT} ${version}; refusing to guess whether it is published."
exit 1
;;
esac
latest=$(curl -sS --fail "https://pypi.org/pypi/${PYPI_PROJECT}/json" \
| python -c 'import json,sys; print(json.load(sys.stdin)["info"]["version"])')
echo "Latest on PyPI: $latest"
python - "$version" "$latest" <<'PY'
import sys
from packaging.version import Version
candidate, latest = Version(sys.argv[1]), Version(sys.argv[2])
if candidate < latest:
sys.exit(
f"::error::pyproject.toml is at {candidate} but PyPI already has {latest}. "
"This branch was generated before that release; regenerate it before merging."
)
PY
emit release true
build:
needs: decide
if: needs.decide.outputs.release == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Sync the version stamps
env:
VERSION: ${{ needs.decide.outputs.version }}
run: |
set -euo pipefail
python - "$VERSION" <<'PY'
import json
import pathlib
import re
import sys
version = sys.argv[1]
wrapper = pathlib.Path("src/whop_sdk/core/client_wrapper.py")
text = wrapper.read_text()
stamped, count = re.subn(
r'("X-Fern-SDK-Version":\s*")[^"]*', rf'\g<1>{version}', text
)
stamped, ua = re.subn(r'"whop_sdk/\d[^"]*"', f'"whop_sdk/{version}"', stamped)
count += ua
if count == 0:
sys.exit(f"::error::no version stamp found in {wrapper}")
wrapper.write_text(stamped)
metadata_path = pathlib.Path(".fern/metadata.json")
if metadata_path.exists():
metadata = json.loads(metadata_path.read_text())
if "requestedVersion" in metadata:
metadata["requestedVersion"] = version
metadata_path.write_text(json.dumps(metadata, indent=2) + "\n")
print(f"stamped {count} occurrence(s) in {wrapper}")
PY
stale=$(grep -rEn '"whop_sdk/[0-9][^"]*"' src \
| grep -v "whop_sdk/${VERSION}" || true)
if [ -n "$stale" ]; then
echo "::error::the source tree still reports a version other than ${VERSION}:"
printf '%s\n' "$stale"
exit 1
fi
- name: Build the distribution
run: |
python -m pip install --disable-pip-version-check --quiet build twine
python -m build --sdist --wheel --outdir dist .
- name: Check the distribution
run: python -m twine check --strict dist/*
- name: Smoke-test the built wheel
env:
VERSION: ${{ needs.decide.outputs.version }}
run: |
set -euo pipefail
python -m venv /tmp/smoke
/tmp/smoke/bin/pip install --disable-pip-version-check --quiet dist/*.whl
/tmp/smoke/bin/python - "$VERSION" <<'PY'
import sys
from importlib import metadata
from whop_sdk import AsyncWhop, Whop
expected = sys.argv[1]
installed = metadata.version("whop-sdk")
if installed != expected:
sys.exit(f"wheel reports {installed}, expected {expected}")
Whop(token="smoke-test")
AsyncWhop(token="smoke-test")
print(f"whop-sdk {installed} imports and constructs")
PY
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
if-no-files-found: error
retention-days: 7
publish:
needs: [decide, build]
if: needs.decide.outputs.release == 'true' && needs.decide.outputs.dry == 'false'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- name: Check the token is present
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: |
set -euo pipefail
if [ -z "${PYPI_TOKEN:-}" ]; then
echo "::error::PYPI_TOKEN is not set on this repository; the upload would fail after the build."
exit 1
fi
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
packages-dir: dist/
print-hash: true
password: ${{ secrets.PYPI_TOKEN }}
tag:
needs: [decide, build, publish]
if: always() && needs.decide.outputs.dry == 'false' && needs.decide.outputs.version != ''
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Tag the published version
env:
VERSION: ${{ needs.decide.outputs.version }}
run: |
set -euo pipefail
status=$(curl -sS -o /dev/null -w '%{http_code}' \
"https://pypi.org/pypi/${PYPI_PROJECT}/${VERSION}/json")
if [ "$status" != "200" ]; then
echo "::notice::${PYPI_PROJECT} ${VERSION} is not on PyPI (HTTP $status); nothing to tag."
exit 0
fi
if git ls-remote --exit-code --tags origin "refs/tags/v${VERSION}" >/dev/null 2>&1; then
echo "v${VERSION} is already tagged."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "v${VERSION}" -m "whop-sdk ${VERSION}"
git push origin "refs/tags/v${VERSION}" || \
git ls-remote --exit-code --tags origin "refs/tags/v${VERSION}" >/dev/null