This project publishes to PyPI via GitHub Actions. The pipeline is designed so every release goes to TestPyPI first, is smoke-tested, and then waits for a maintainer to approve the production publish.
release.yml (manual)
└─► bump version + changelog + tag + GitHub Release
└─► publish-to-pypi.yml (triggered by Release publish)
├─► build (build wheel + sdist once, bundle Web UI)
├─► publish-testpypi (environment: testpypi, OIDC)
├─► smoke-test (install from TestPyPI, verify entry points)
└─► publish-pypi (environment: pypi — MAINTAINER APPROVAL GATE)
Authentication uses PyPI Trusted Publishing (OIDC) — no API token secrets are stored in GitHub.
PyPI:
- Go to https://pypi.org/manage/account/publishing/
- "Add a new pending publisher" with:
- PyPI project name:
cli-agent-orchestrator - Owner:
awslabs - Repository:
cli-agent-orchestrator - Workflow:
publish-to-pypi.yml - Environment:
pypi
- PyPI project name:
TestPyPI:
- Go to https://test.pypi.org/manage/account/publishing/
- Same values, but environment:
testpypi
In GitHub: Settings → Environments.
testpypi — no restrictions. (Smoke-test runs here; no approval needed.)
pypi — this is the approval gate:
- Required reviewers: add the maintainer team / usernames who can approve prod releases.
- Deployment branches and tags: restrict to tags matching
v*so only tagged releases can promote to prod.
Nothing to configure — the build job runs npm ci && npm run build in web/
and copies web/dist/ into src/cli_agent_orchestrator/web_ui/ before uv build.
The wheel artifact includes this per [tool.hatch.build].artifacts in
pyproject.toml.
- Trigger
Releaseworkflow via Actions → "Run workflow":- Pick
patch,minor, ormajor release.ymlbumpspyproject.toml, runsgit-cliffto updateCHANGELOG.md, commits, tagsv<version>, pushes, and creates a GitHub Release.
- Pick
- GitHub Release publish automatically triggers
Publish to PyPI:buildruns (wheel + sdist with Web UI bundled).publish-testpypipublishes to TestPyPI.smoke-testinstalls from TestPyPI and runscao --help,cao-server --help,cao-mcp-server --help.publish-pypipauses waiting for a maintainer to approve in thepypienvironment. A required reviewer clicks Review deployments → Approve on the Actions run to ship to prod.
Sometimes you want to sanity-check a build without cutting a real release:
- Actions → Publish to PyPI → "Run workflow"
- Select
environment: testpypi - Only
build+publish-testpypirun. Smoke-test and prod steps are skipped.
If a release was cut but the auto-pipeline failed partway through and you need to retry the prod step:
- Actions → Publish to PyPI → "Run workflow"
- Select
environment: pypi buildruns,publish-testpypi+smoke-testare skipped (they already ran), andpublish-pypihits the maintainer approval gate.
"pending publisher" error on first publish: Expected. PyPI marks the publisher pending until the first successful run. After that it becomes permanent.
Smoke-test fails with "No matching distribution": TestPyPI index can take up to a minute to propagate. The workflow sleeps 30s; if that's not enough, increase it or retry.
Approval required but button missing:
Check Settings → Environments → pypi → Required reviewers. You must be listed
there.
Version mismatch between wheel and tag:
scripts/bump_version.py updates pyproject.toml and release.yml tags on the
bumped version. If drift occurs, fix pyproject.toml manually and re-run the
release workflow.