diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..357434a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +name: release + +# Triggers PyPI publish when a tag matching `v*` is pushed. +# Uses PyPI trusted-publisher (OIDC) so no secrets need to live in +# this repo. Setup, one-time, on PyPI side: +# pypi.org → manage cacp-protocol → publishing → add trusted publisher +# owner: zenprocess +# repository: cacp +# workflow: release.yml +# environment: pypi +# +# Fallback: if trusted-publisher is not configured, the upload step +# fails with a loud "trusted publisher not registered" error. Add +# `PYPI_API_TOKEN` as a repo secret and replace the `with:` block on +# the publish step with `password: ${{ secrets.PYPI_API_TOKEN }}` to +# unblock without OIDC. + +on: + push: + tags: + - "v*" + +permissions: + contents: read + +jobs: + build: + name: Build sdist + wheel + runs-on: ubuntu-latest + defaults: + run: + working-directory: cacp-python + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install build tooling + run: python -m pip install --upgrade pip build + - name: Build distributions + run: python -m build + - name: Verify version matches the tag + run: | + set -eu + DIST_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + TAG_VERSION="${GITHUB_REF_NAME#v}" + if [ "$DIST_VERSION" != "$TAG_VERSION" ]; then + echo "::error::pyproject version $DIST_VERSION does not match tag v$TAG_VERSION" + exit 1 + fi + - uses: actions/upload-artifact@v4 + with: + name: dist + path: cacp-python/dist/ + + publish: + name: Publish to PyPI + needs: build + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write # required for trusted-publisher OIDC + steps: + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/cacp-python/README.md b/cacp-python/README.md index 4561a33..9cbc6a8 100644 --- a/cacp-python/README.md +++ b/cacp-python/README.md @@ -1,14 +1,18 @@ -# cacp — Reference Python parser for CACP +# cacp-protocol — Reference Python parser for CACP Canonical Python reference implementation of [CACP (Compressed Agent Communication Protocol)](https://github.com/zenprocess/cacp). The spec lives in the parent [`README.md`](../README.md); this package implements the parser against it. +> **Naming**: distributed on PyPI as `cacp-protocol` (the unprefixed +> `cacp` slot was already taken by an unrelated project — Classification +> Algorithms Comparison Pipeline). Imports as `cacp_protocol`. + ## Install ``` -pip install cacp +pip install cacp-protocol ``` Zero runtime dependencies — pure stdlib. @@ -16,7 +20,7 @@ Zero runtime dependencies — pure stdlib. ## Usage ```python -from cacp import parse +from cacp_protocol import parse text = open("agent_response.txt").read() response = parse(text) diff --git a/cacp-python/pyproject.toml b/cacp-python/pyproject.toml index f9f6e85..b78cb1f 100644 --- a/cacp-python/pyproject.toml +++ b/cacp-python/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "cacp" +name = "cacp-protocol" version = "0.1.0" description = "Reference Python parser for CACP (Compressed Agent Communication Protocol)" readme = "README.md" diff --git a/cacp-python/src/cacp/__init__.py b/cacp-python/src/cacp_protocol/__init__.py similarity index 82% rename from cacp-python/src/cacp/__init__.py rename to cacp-python/src/cacp_protocol/__init__.py index ea098cf..1823555 100644 --- a/cacp-python/src/cacp/__init__.py +++ b/cacp-python/src/cacp_protocol/__init__.py @@ -3,8 +3,8 @@ See the canonical spec at https://github.com/zenprocess/cacp. """ -from cacp.parser import parse -from cacp.models import ( +from cacp_protocol.parser import parse +from cacp_protocol.models import ( CACPResponse, CANONICAL_STATUS_VALUES, CANONICAL_TESTS_BUILD_VALUES, diff --git a/cacp-python/src/cacp/models.py b/cacp-python/src/cacp_protocol/models.py similarity index 100% rename from cacp-python/src/cacp/models.py rename to cacp-python/src/cacp_protocol/models.py diff --git a/cacp-python/src/cacp/parser.py b/cacp-python/src/cacp_protocol/parser.py similarity index 99% rename from cacp-python/src/cacp/parser.py rename to cacp-python/src/cacp_protocol/parser.py index aed1d4c..53460fe 100644 --- a/cacp-python/src/cacp/parser.py +++ b/cacp-python/src/cacp_protocol/parser.py @@ -17,7 +17,7 @@ import re -from cacp.models import ( +from cacp_protocol.models import ( CACPResponse, CANONICAL_STATUS_VALUES, CANONICAL_TESTS_BUILD_VALUES, diff --git a/cacp-python/tests/test_conformance.py b/cacp-python/tests/test_conformance.py index a0953a9..a6fa59b 100644 --- a/cacp-python/tests/test_conformance.py +++ b/cacp-python/tests/test_conformance.py @@ -9,7 +9,7 @@ import pytest -from cacp import ( +from cacp_protocol import ( CANONICAL_STATUS_VALUES, CANONICAL_TESTS_BUILD_VALUES, parse, diff --git a/cacp-python/tests/test_roundtrip.py b/cacp-python/tests/test_roundtrip.py index 722527f..68ce0a5 100644 --- a/cacp-python/tests/test_roundtrip.py +++ b/cacp-python/tests/test_roundtrip.py @@ -7,7 +7,7 @@ from __future__ import annotations -from cacp import parse +from cacp_protocol import parse # Verbatim copy of the response example in the parent README.md (§"Response