Skip to content

Commit f060a49

Browse files
LukasParkeclaude
andauthored
ci(publish): drop the TestPyPI target (#25)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent fdf1004 commit f060a49

2 files changed

Lines changed: 35 additions & 39 deletions

File tree

‎.github/workflows/publish.yaml‎

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,25 @@ name: Publish
1010
# Release procedure:
1111
# 1. Land the version bump (pyproject.toml `version`). For a port sync that is
1212
# done by scripts/upstream; otherwise edit it in a PR.
13-
# 2. Run this workflow with target=testpypi to rehearse (optional but cheap).
14-
# 3. Run with target=pypi, dry-run=true, and read the summary.
15-
# 4. Run with target=pypi, dry-run=false to release.
13+
# 2. Run with dry-run=true and read the summary.
14+
# 3. Run with dry-run=false to release.
15+
#
16+
# There is deliberately no TestPyPI target. It required a second, separate
17+
# trusted-publisher registration on test.pypi.org (its own site, own account, own
18+
# publisher config), and having only the pypi.org one configured meant the
19+
# rehearsal failed with `invalid-publisher` while the real path was fine — a
20+
# rehearsal that fails for reasons the real run would not have is worse than no
21+
# rehearsal. The dry run below covers what the rehearsal was actually for:
22+
# everything except the upload itself.
1623
#
1724
# One-time setup on PyPI, before the first real publish — the workflow cannot do
1825
# this for you:
1926
# PyPI → the project (or "pending publisher" if it does not exist yet) →
2027
# Publishing → add a GitHub trusted publisher with
2128
# owner: OpenRouterTeam repo: python-agent
2229
# workflow: publish.yaml environment: pypi
23-
# Then create the `pypi` (and `testpypi`) environment in repo Settings, and set
24-
# its deployment branch policy to `main`.
30+
# Then create the `pypi` environment in repo Settings and set its deployment
31+
# branch policy to `main`.
2532
#
2633
# That environment branch policy is the real ref restriction. The `if:` guard
2734
# below stops accidents, not a determined actor: workflow_dispatch runs the
@@ -33,14 +40,6 @@ name: Publish
3340
on:
3441
workflow_dispatch:
3542
inputs:
36-
target:
37-
description: "Index to publish to. Rehearse on testpypi first."
38-
required: true
39-
type: choice
40-
options:
41-
- testpypi
42-
- pypi
43-
default: testpypi
4443
dry-run:
4544
description: "Build and verify, but do not upload. Leave enabled until you have read the summary."
4645
required: false
@@ -51,7 +50,7 @@ permissions:
5150
contents: read
5251

5352
concurrency:
54-
group: publish-${{ inputs.target }}
53+
group: publish
5554
cancel-in-progress: false
5655

5756
jobs:
@@ -61,7 +60,7 @@ jobs:
6160
# Selects the trusted-publisher identity and, via its deployment branch
6261
# policy, restricts which refs may publish. A dry run still targets the
6362
# environment so an approval gate is exercised in rehearsal too.
64-
environment: ${{ inputs.target }}
63+
environment: pypi
6564
permissions:
6665
contents: read
6766
id-token: write # OIDC token exchange for trusted publishing
@@ -155,11 +154,7 @@ jobs:
155154
set -euo pipefail
156155
VERSION="$(uv run python -c "import importlib.metadata as m; print(m.version('openrouter-agent-sdk'))")"
157156
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
158-
if [ "${{ inputs.target }}" = "pypi" ]; then
159-
INDEX="https://pypi.org/pypi/openrouter-agent-sdk/json"
160-
else
161-
INDEX="https://test.pypi.org/pypi/openrouter-agent-sdk/json"
162-
fi
157+
INDEX="https://pypi.org/pypi/openrouter-agent-sdk/json"
163158
# Collision test done in Python, not by word-splitting a shell string:
164159
# the shell form is subtly non-portable (zsh does not split unquoted
165160
# variables the way bash does), and a guard that silently stops
@@ -175,21 +170,21 @@ jobs:
175170
PY
176171
status=$?
177172
if [ "$status" -eq 2 ]; then
178-
echo "::error::Version $VERSION is already published on ${{ inputs.target }}. A PyPI version can never be reused — bump the version in pyproject.toml."
173+
echo "::error::Version $VERSION is already published on PyPI. A version can never be reused — bump the version in pyproject.toml."
179174
exit 1
180175
elif [ "$status" -ne 0 ]; then
181176
echo "::error::Could not determine published versions (exit $status). Refusing to publish blind."
182177
exit 1
183178
fi
184179
else
185-
echo "Project not on ${{ inputs.target }} yet — this would be the first release."
180+
echo "Project not on PyPI yet — this would be the first release."
186181
fi
187-
echo "Version $VERSION is publishable on ${{ inputs.target }}."
182+
echo "Version $VERSION is publishable on PyPI."
188183
189184
- name: Summary
190185
run: |
191186
{
192-
echo "## Publish ${{ inputs.target }}"
187+
echo "## Publish to PyPI"
193188
echo
194189
echo "- Version: \`${{ steps.version.outputs.version }}\`"
195190
echo "- Dry run: **${{ inputs.dry-run }}**"
@@ -199,7 +194,7 @@ jobs:
199194
echo "Nothing was uploaded. Artifacts were built and verified only."
200195
echo "Re-run with dry-run disabled to publish."
201196
else
202-
echo "Uploading to ${{ inputs.target }}."
197+
echo "Uploading to PyPI."
203198
fi
204199
echo
205200
echo '```'
@@ -211,18 +206,11 @@ jobs:
211206
# be downloaded and inspected.
212207
- uses: actions/upload-artifact@v4
213208
with:
214-
name: dist-${{ inputs.target }}-${{ steps.version.outputs.version }}
209+
name: dist-${{ steps.version.outputs.version }}
215210
path: dist/
216211

217-
- name: Publish to TestPyPI
218-
if: inputs.target == 'testpypi' && inputs.dry-run == false
219-
uses: pypa/gh-action-pypi-publish@release/v1
220-
with:
221-
repository-url: https://test.pypi.org/legacy/
222-
print-hash: true
223-
224212
- name: Publish to PyPI
225-
if: inputs.target == 'pypi' && inputs.dry-run == false
213+
if: inputs.dry-run == false
226214
uses: pypa/gh-action-pypi-publish@release/v1
227215
with:
228216
print-hash: true

‎PORTING.md‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,27 @@ this repo.
189189

190190
```
191191
1. Land the version bump in pyproject.toml (a port sync does this).
192-
2. Run Publish with target=testpypi to rehearse.
193-
3. Run with target=pypi, dry-run=true — read the summary.
194-
4. Run with target=pypi, dry-run=false to release.
192+
2. Run Publish with dry-run=true — read the summary.
193+
3. Run with dry-run=false to release.
195194
```
196195

196+
There is deliberately **no TestPyPI rehearsal**. TestPyPI is a separate site with
197+
its own account and its own trusted-publisher config, so it needed a second
198+
registration; with only the pypi.org one in place the rehearsal failed with
199+
`invalid-publisher` while the real path was fine. A rehearsal that fails for
200+
reasons the real run would not is worse than no rehearsal. The dry run covers what
201+
it was actually for — verify, build, `twine check --strict`, isolated wheel import,
202+
and the already-published and ahead-of-release guards — everything except the
203+
upload itself.
204+
197205
Before the first real publish, two things must be set up by hand — the workflow
198206
cannot do them for you:
199207

200208
1. **On PyPI**: add a GitHub trusted publisher (owner `OpenRouterTeam`, repo
201209
`python-agent`, workflow `publish.yaml`, environment `pypi`). If the project
202210
does not exist yet, add it as a *pending* publisher.
203-
2. **In repo Settings**: create the `pypi` and `testpypi` environments and set each
204-
one's deployment branch policy to `main`.
211+
2. **In repo Settings**: create the `pypi` environment and set its deployment
212+
branch policy to `main`.
205213

206214
That branch policy is the real ref restriction. PyPI's trusted publisher pins
207215
owner/repo/workflow/environment but carries no branch claim, and

0 commit comments

Comments
 (0)