Skip to content

Commit 21b9b5b

Browse files
njbrakeclaude
andauthored
feat: independent release automation + surface gateway spec version (#12)
* feat: surface the gateway spec version as otari.__spec_version__ The gateway codegen now stamps the spec version into the generated core (otari._client._spec_version). Re-export it from the package so callers can tell which gateway spec this SDK targets. The marker is seeded here to match the codegen output byte-for-byte, so the next regeneration is a no-op for it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: automate releases with release-please Replace the manual "create a GitHub release by hand" flow with release-please. Merges to main accumulate into a release PR that bumps the version and updates CHANGELOG.md; merging it tags the release and publishes to PyPI in the same workflow run (gated on release_created), so no PAT is needed to chain off the release event. Removes the old publish.yml, whose job moves into the publish step here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add RELEASE.md describing the release-please flow Document how this SDK is released: conventional commits drive a release-please release PR; merging it tags and publishes. Covers the repo's registry, secret, version file, the one-time repo setting, and the retry path, and links to the gateway compatibility doc for the cross-repo policy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6704e56 commit 21b9b5b

8 files changed

Lines changed: 125 additions & 57 deletions

File tree

.github/workflows/publish.yml

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Release
2+
3+
# Independent release automation for this SDK (decoupled from the gateway's own
4+
# releases). release-please watches main: each merged conventional commit
5+
# accumulates into a release PR that bumps the version in pyproject.toml and
6+
# updates CHANGELOG.md. Merging that release PR tags the release and creates a
7+
# GitHub Release, and the publish job below ships it to PyPI in the same run
8+
# (so no PAT is needed to chain a separate workflow off the release event).
9+
10+
on:
11+
push:
12+
branches: [main]
13+
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
jobs:
19+
release-please:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
release_created: ${{ steps.release.outputs.release_created }}
23+
steps:
24+
- uses: googleapis/release-please-action@v4
25+
id: release
26+
with:
27+
token: ${{ secrets.GITHUB_TOKEN }}
28+
29+
publish:
30+
needs: release-please
31+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
32+
runs-on: ubuntu-latest
33+
environment: pypi
34+
permissions:
35+
contents: read
36+
id-token: write
37+
steps:
38+
- uses: actions/checkout@v4
39+
40+
- uses: astral-sh/setup-uv@v6
41+
42+
- name: Build package
43+
# release-please already committed the version bump to pyproject.toml on
44+
# main, so the build picks it up with no tag-to-file patching needed.
45+
run: uv build
46+
47+
- name: Publish to PyPI
48+
uses: pypa/gh-action-pypi-publish@release/v1

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "0.1.0"
3+
}

RELEASE.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Releasing
2+
3+
This SDK is versioned independently of the otari gateway, with its own semver.
4+
Releases are automated with [release-please](https://github.com/googleapis/release-please).
5+
6+
## How a release happens
7+
8+
1. Merge changes to `main` using [Conventional Commits](https://www.conventionalcommits.org/)
9+
(`feat:`, `fix:`, etc.). This includes the gateway codegen's regeneration PRs
10+
and ordinary shell PRs.
11+
2. release-please opens or updates a single **release PR** that bumps the version
12+
in `pyproject.toml` (`[project].version`) and writes `CHANGELOG.md`.
13+
3. Review and merge the release PR. That tags the release and creates a GitHub
14+
Release.
15+
4. The same workflow run (`.github/workflows/release-please.yml`, gated on
16+
`release_created`) builds the package and publishes it to PyPI.
17+
18+
## Configuration
19+
20+
- **Registry:** PyPI (`otari`).
21+
- **Auth:** PyPI trusted publishing via OIDC (`environment: pypi`, `id-token: write`).
22+
No API token secret is stored; the trusted publisher must be configured on PyPI.
23+
- **Version file:** `pyproject.toml` `[project].version` (the only place to change
24+
the version; do not edit it by hand, release-please owns it).
25+
26+
## Prerequisites (one time, repo settings)
27+
28+
- Enable **Settings to Actions: "Allow GitHub Actions to create and approve pull
29+
requests"** so release-please can open its release PR.
30+
- Configure the PyPI trusted publisher for this repo and the `pypi` environment.
31+
32+
## If the publish fails
33+
34+
The release tag and GitHub Release already exist, so re-run the failed `publish`
35+
job from the Actions tab to retry publishing the same version. Avoid cutting a
36+
release by hand; the automated path keeps `pyproject.toml`, the tag, and the
37+
changelog in sync.
38+
39+
See the gateway's [SDK release coordination and compatibility](https://github.com/mozilla-ai/otari/blob/main/docs/sdk-compatibility.md)
40+
for the cross-repo policy, the spec-version model, and the end-to-end flow.

release-please-config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3+
"include-v-in-tag": false,
4+
"packages": {
5+
".": {
6+
"release-type": "python",
7+
"package-name": "otari"
8+
}
9+
}
10+
}

src/otari/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
from importlib.metadata import PackageNotFoundError, version
2020

21+
# Gateway/spec version the generated core was built from, stamped into the core by
22+
# the gateway codegen pipeline. Surfaced here so callers can check which gateway
23+
# spec this SDK targets (see https://github.com/mozilla-ai/otari spec compatibility).
24+
from otari._client._spec_version import __spec_version__ as __spec_version__
2125
from otari.async_client import AsyncOtariClient
2226
from otari.client import OtariClient
2327
from otari.control_plane import ControlPlane

src/otari/_client/_spec_version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__spec_version__ = "0.0.0-dev"

tests/unit/test_spec_version.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""The gateway/spec version is surfaced on the public package.
2+
3+
The gateway codegen stamps the spec version into the generated core
4+
(``otari._client._spec_version``); the package re-exports it as
5+
``otari.__spec_version__`` so callers can tell which gateway spec this SDK
6+
targets. This guards the wiring (a stale literal in ``__init__`` would diverge
7+
from the generated marker).
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import otari
13+
from otari._client._spec_version import __spec_version__ as marker
14+
15+
16+
def test_spec_version_is_surfaced() -> None:
17+
assert otari.__spec_version__ == marker
18+
assert isinstance(otari.__spec_version__, str)
19+
assert otari.__spec_version__

0 commit comments

Comments
 (0)