Release and package publishing
OTerminus release publishing is intentionally staged so the first public PyPI releases are repeatable and safe.
Release notes are tracked in the root
CHANGELOG.md. Keep the changelog
entry for the target version accurate before tagging a release; do not mark a version as released
until the production PyPI release is complete.
Release checklist
Use this checklist for each release, starting with 0.1.2. The workflow details below remain the
source of truth for TestPyPI, production PyPI, Trusted Publishing, and protected environments.
Pre-release
- Confirm all target issues and pull requests for the release are merged.
- Update
CHANGELOG.mdfor the target version. - Update
pyproject.tomlto the target version. - Run the full local regression gate:
poetry run pytest
poetry run ruff check .
poetry run ruff format --check .
poetry run oterminus-evals
poetry run python scripts/generate_command_reference.py --check
poetry run python scripts/check_docs_links.py
cd website
npm ci
npm run build
npm run typecheck
The deterministic eval gate includes release_smoke.json, which protects public-install and
first-use proposal/validation flows without calling Ollama, touching the network, executing shell
commands, or requiring a real installed package. Keep doctor and version readiness covered by CLI
tests and installed-package smoke checks rather than eval fixtures.
- Build the package:
poetry build
- Validate local wheel installation:
poetry run python scripts/validate_package_install.py
- Optionally validate TestPyPI when packaging metadata, package contents, console scripts, or install behavior changed.
If scripts/generate_command_reference.py or scripts/check_docs_links.py is not present on a
future branch, skip the missing script and document the reason in the release PR.
Release
- Commit the version and changelog updates together.
- Create and push a matching tag, for example
v0.1.2. - Confirm the production PyPI workflow validates tag/version consistency.
- Approve the protected
pypienvironment deployment if configured.
Post-release
- Verify the PyPI project page at
https://pypi.org/project/oterminus/. - Verify either a fresh
pipxinstall or an upgrade of an existingpipxinstall:
pipx install oterminus
oterminus --version
oterminus doctor
oterminus-evals
pipx upgrade oterminus
oterminus --version
oterminus doctor
oterminus-evals
- Verify the pip fallback install in a clean virtual environment:
python -m pip install oterminus
oterminus --version
oterminus doctor
oterminus-evals
- Update GitHub release notes if using GitHub Releases.
Workflow map
- Main CI workflow:
.github/workflows/ci.yml- trigger: pull requests and pushes to
main - Ubuntu job is the full regression gate
- macOS job is a focused platform smoke lane for platform-aware registry behavior and installed CLI basics
- does not publish to TestPyPI or PyPI
- trigger: pull requests and pushes to
- TestPyPI validation workflow:
.github/workflows/publish-testpypi.yml- trigger: manual
workflow_dispatch - does not run on pull requests
- publishes only to TestPyPI
- trigger: manual
- Production PyPI workflow:
.github/workflows/publish-pypi.yml- trigger: pushed tags matching
v* - does not run on pull requests
- does not run on normal
mainpushes
- trigger: pushed tags matching
What is automatic (workflows)
TestPyPI workflow automation
The TestPyPI workflow automatically:
- installs dependencies with Poetry
- runs release checks:
poetry run pytestpoetry run ruff check .poetry run ruff format --check .cd website && npm ci && npm run build && npm run typecheckpoetry run oterminus-evals- generated reference-docs check (if script exists)
- docs link checker (if script exists)
- builds distributions via
poetry build - publishes artifacts to TestPyPI via OIDC Trusted Publishing
- creates a clean virtualenv, installs the exact published version from TestPyPI (with short
retries for index propagation), and runs smoke checks:
oterminus --helpoterminus --versionoterminus doctoroterminus-evals
Install command used post-publish:
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "oterminus==<resolved-version>"
Production PyPI workflow automation
The production workflow automatically:
- validates tag/version consistency (
vX.Y.Zmust matchtool.poetry.version) - runs the full regression gate:
poetry run pytestpoetry run ruff check .poetry run ruff format --check .cd website && npm ci && npm run build && npm run typecheckpoetry run oterminus-evals- generated reference-docs check
- docs link checker
- runs local installed-package validation with
poetry run python scripts/validate_package_install.py - uploads the validated
dist/artifacts - publishes the exact validated artifacts to PyPI after environment approval
Authentication is OIDC Trusted Publishing (id-token: write) with
pypa/gh-action-pypi-publish; no long-lived PyPI API tokens are required.
Local package validation
Before publishing or changing packaging behavior, validate local artifacts from a source checkout:
poetry run python scripts/validate_package_install.py
This contributor/release-maintainer check builds the local sdist and wheel, installs the wheel
into a temporary clean virtual environment, verifies import oterminus, and runs installed-console
smoke checks such as oterminus --help, oterminus --version, oterminus version,
oterminus doctor, config path/init/get/set/reset/validate/show checks, shell completion rendering,
and oterminus-evals. oterminus doctor may exit non-zero in CI when Ollama is unavailable; the
validation still confirms the installed CLI is callable and installable. The script uses temporary
paths for smoke-check config, audit, and history files and does not publish anything.
It is not the primary end-user install path; users should install released packages from PyPI with
pipx install oterminus or, when pipx is unavailable, python -m pip install oterminus.
The main CI workflow runs the same package validation command in the Ubuntu full regression gate after the normal tests, lint, evals, generated docs checks, docs link checks, and Docusaurus docs build. Its macOS smoke lane also runs the package validation command to verify installed CLI basics on a real macOS runner without requiring Ollama or publishing artifacts. The production release workflow runs package validation before uploading artifacts for PyPI publishing. The TestPyPI workflow still verifies the exact published version by installing it back from TestPyPI after publish.
Post-release user install verification
After a production PyPI release is available, verify the preferred end-user installation path with
pipx from a clean environment:
pipx install oterminus
oterminus --help
oterminus --version
oterminus doctor
oterminus-evals
Also verify the pip fallback in a clean virtual environment:
python -m pip install oterminus
oterminus --help
oterminus --version
oterminus doctor
For an existing pipx install, verify upgrades with:
pipx upgrade oterminus
oterminus --version
oterminus doctor
The published package name is oterminus; the installed console scripts are oterminus and
oterminus-evals. oterminus --version confirms the installed package version without requiring
Ollama; oterminus doctor is the recommended post-install readiness diagnostic and should clearly
report installed package version, Python executable, install context, Ollama CLI, service,
local-model readiness, selected model, and config/audit/history path status. PyPI installation does
not install or start Ollama; natural-language planning still depends on a ready local Ollama setup,
although direct commands and retained deterministic_shortcut utility requests may not need a live
model.
Release verification should not add or rely on automatic shell startup-file changes. OTerminus
supports REPL Tab autocomplete via prompt_toolkit and prints opt-in zsh, bash, and fish
shell-level completion scripts with oterminus completion <shell>.
Note: oterminus doctor may exit non-zero in clean or CI environments if Ollama is unavailable;
this still confirms the installed CLI is callable and that readiness failures are surfaced clearly.
What remains manual and intentional
The following steps are intentionally manual for release control:
- choose next release version
- update
CHANGELOG.md - update
pyproject.tomlversion - run the full local regression gate
- build package distributions and validate local wheel installation
- commit and push release version/changelog changes
- create and push
v*release tag - approve deployment in GitHub
pypienvironment
Manual release trigger commands:
poetry version X.Y.Z
git add CHANGELOG.md pyproject.toml poetry.lock
git commit -m "Release vX.Y.Z"
git push origin main
git tag vX.Y.Z
git push origin vX.Y.Z
These version/tag commands are intentionally manual for now.
One-time Trusted Publisher + environment setup
Create pending Trusted Publishers
For TestPyPI:
- project/package:
oterminus - owner:
PooriaT - repository:
oterminus - workflow filename:
publish-testpypi.yml - environment:
testpypi
For PyPI:
- project/package:
oterminus - owner:
PooriaT - repository:
oterminus - workflow filename:
publish-pypi.yml - environment:
pypi
Create GitHub environments
In repository Settings → Environments:
- create
testpypi - create
pypi - require manual approval for
pypi - optionally restrict deployment branches/tags to release patterns
Do not add long-lived PYPI_API_TOKEN secrets when Trusted Publishing is configured.