diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..53c2aa1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Release + +# Publishes py-sendly to PyPI when a version tag (vX.Y.Z) is pushed. +# Disjoint from ci.yml (push/PR to main + weekly cron): this runs ONLY on tags, +# re-runs the full gate, then publishes. A red SDK can never ship. +on: + push: + tags: ["v*"] + +permissions: + contents: read + +jobs: + publish: + name: Test, build & publish to PyPI + runs-on: ubuntu-latest + environment: pypi # must match the environment on the PyPI trusted publisher + permissions: + id-token: write # OIDC for PyPI Trusted Publishing (no token stored) + steps: + - uses: actions/checkout@v6.0.3 + + - uses: actions/setup-python@v5.6.0 + with: + python-version: "3.13" + + - name: Install build + dev toolchain + run: | + python -m pip install --upgrade pip build + pip install -e ".[dev]" + + # Version-consistency gate: the pushed tag must equal both the + # pyproject.toml version and the SDK_VERSION constant (User-Agent header). + - name: Assert tag matches pyproject + SDK_VERSION + run: | + TAG="${GITHUB_REF_NAME#v}" + VER=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + SDK=$(python -c "from sendly import SDK_VERSION;print(SDK_VERSION)") + echo "tag=$TAG pyproject=$VER SDK_VERSION=$SDK" + test "$TAG" = "$VER" || { echo "::error::tag $TAG != pyproject $VER"; exit 1; } + test "$TAG" = "$SDK" || { echo "::error::tag $TAG != SDK_VERSION $SDK"; exit 1; } + + - name: Ruff lint + run: ruff check . + + - name: Ruff format check + run: ruff format --check . + + - name: Mypy (strict) + run: mypy src + + - name: Contract tests (SDK surface vs vendored OpenAPI) + run: pytest tests/test_contract.py + + - name: Pytest + run: pytest + + - name: Build sdist + wheel + run: python -m build + + - name: Publish to PyPI (Trusted Publishing / OIDC) + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/README.md b/README.md index 1de62ba..cd89163 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,17 @@ suppression. ## Installation -Not yet published to PyPI. Install from GitHub: +```bash +pip install py-sendly +``` + +The distribution is published as `py-sendly`; the import name is unchanged: + +```python +import sendly +``` + +Alternatively, install the latest `main` directly from GitHub: ```bash pip install git+https://github.com/DevinoSolutions/sendly-python.git diff --git a/pyproject.toml b/pyproject.toml index 39efad0..b4b64e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,10 @@ requires = ["hatchling==1.30.1"] build-backend = "hatchling.build" [project] -name = "sendly" +# PyPI distribution name. The plain `sendly` name is owned by an unrelated +# SMS company on PyPI, so we publish as `py-sendly`. The import package is +# unchanged (`import sendly`) — see [tool.hatch.build.targets.wheel] below. +name = "py-sendly" version = "0.1.0" description = "Official Sendly Python SDK" readme = "README.md"