Skip to content

Commit b2c234b

Browse files
committed
ci: add python publish workflow
1 parent 0ae2907 commit b2c234b

2 files changed

Lines changed: 115 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Publish Python package
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
repository:
7+
description: Python package index to publish.
8+
required: true
9+
default: testpypi
10+
type: choice
11+
options:
12+
- testpypi
13+
- pypi
14+
15+
jobs:
16+
publish:
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: "3.11"
27+
28+
- name: Setup uv
29+
uses: astral-sh/setup-uv@v5
30+
with:
31+
enable-cache: true
32+
33+
- name: Install dependencies
34+
run: uv sync --all-groups
35+
36+
- name: Run tests
37+
run: uv run pytest -q
38+
39+
- name: Ruff
40+
run: uv run ruff check .
41+
42+
- name: mypy
43+
run: uv run mypy src/calle
44+
45+
- name: Build package
46+
run: uv build
47+
48+
- name: Check distribution metadata
49+
run: uvx twine check dist/*
50+
51+
- name: Publish
52+
env:
53+
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
54+
TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
55+
run: |
56+
set -euo pipefail
57+
58+
if [ "${{ inputs.repository }}" = "pypi" ]; then
59+
token="$PYPI_API_TOKEN"
60+
repository_url="https://upload.pypi.org/legacy/"
61+
else
62+
token="$TEST_PYPI_API_TOKEN"
63+
repository_url="https://test.pypi.org/legacy/"
64+
fi
65+
66+
if [ -z "$token" ]; then
67+
echo "::error::Missing package index token for ${{ inputs.repository }}."
68+
exit 1
69+
fi
70+
71+
TWINE_USERNAME="__token__" TWINE_PASSWORD="$token" \
72+
uvx twine upload --repository-url "$repository_url" dist/*
73+
74+
- name: Verify published package
75+
run: |
76+
set -euo pipefail
77+
78+
if [ "${{ inputs.repository }}" = "pypi" ]; then
79+
url="https://pypi.org/pypi/calle-ai/json"
80+
else
81+
url="https://test.pypi.org/pypi/calle-ai/json"
82+
fi
83+
84+
curl --fail --silent --show-error "$url" >/dev/null

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,34 @@ event = client.webhooks.unwrap(
5757
secret=os.environ["CALLE_WEBHOOK_SECRET"],
5858
)
5959
```
60+
61+
## Release
62+
63+
This repository publishes the Python distribution `calle-ai`. Application code
64+
imports it as `calle`.
65+
66+
Prerequisites:
67+
68+
- Create a TestPyPI API token and add it as the GitHub Actions secret
69+
`TEST_PYPI_API_TOKEN`.
70+
- Create a PyPI API token and add it as the GitHub Actions secret
71+
`PYPI_API_TOKEN`.
72+
- Keep the package version in `pyproject.toml` unique before each publish.
73+
74+
Manual TestPyPI rehearsal:
75+
76+
1. Open the `Publish Python package` GitHub Actions workflow.
77+
2. Run it from `main` with repository `testpypi`.
78+
3. Verify install in a temporary environment:
79+
80+
```bash
81+
python -m venv .venv
82+
. .venv/bin/activate
83+
pip install --index-url https://test.pypi.org/simple/ \
84+
--extra-index-url https://pypi.org/simple \
85+
calle-ai==0.1.0b1
86+
python -c 'from calle import CalleClient; print(CalleClient)'
87+
```
88+
89+
Use repository `pypi` only after the TestPyPI package has been installed and
90+
tested.

0 commit comments

Comments
 (0)