Releasing

How to cut a new version of eVi.

One-time setup

  1. PyPI Trusted Publishing. On https://pypi.org → Manage → Publishing, add a pending publisher for the evi-assistant distribution (the import package and CLI stay evi; only the PyPI name is evi-assistant) pointing at this repo's release.yml workflow. No API tokens required.
  2. Docker Hub / GHCR (optional). If you want to push the image too, add DOCKER_USERNAME / DOCKER_TOKEN secrets and a job to release.yml (not enabled by default — see Roadmap below).

Cutting a release

# 1. Bump the version in two places (they're tripwired by the release CI):
#      pyproject.toml → [project] version
#      evi/__init__.py → __version__
#
# 2. Update CHANGELOG.md — add a section under the new version with the
#    notable changes. Phase memory ($CLAUDE_HOME/.claude/.../project_evi.md)
#    is a good source of truth.
#
# 3. Commit, tag, push.
git add pyproject.toml evi/__init__.py CHANGELOG.md
git commit -m "release: 0.8.0"
git tag v0.8.0
git push origin main --tags

The release.yml workflow:

  1. Verifies the git tag matches both pyproject.toml's version and evi/__init__.py's __version__ (fails on any drift).
  2. Installs the package + all useful extras.
  3. Runs the full test suite.
  4. Builds sdist + wheel via python -m build.
  5. Publishes to PyPI via Trusted Publishing (OIDC — no API token).
  6. Keyless (OIDC) sigstore-signs the artifacts and creates a GitHub release with auto-generated notes + attached artifacts (wheel/sdist + *.sigstore.json).

If anything fails before publish, fix it on a new commit and retag with a patched version (v0.8.1). Don't re-push the same tag — GitHub releases get confused and PyPI rejects duplicates.

Local verification before tagging

# Test
.venv/Scripts/python -m pytest -q --timeout=30

# Lint
.venv/Scripts/python -m ruff check evi tests scripts

# Build
.venv/Scripts/python -m build
# Outputs to dist/. Both sdist + wheel should appear.

# Smoke-test the wheel in a fresh venv:
python -m venv /tmp/wheel-check
/tmp/wheel-check/bin/python -m pip install ./dist/evi_assistant-X.Y.Z-py3-none-any.whl
/tmp/wheel-check/bin/evi --version

Desktop installers (separate pipeline)

The Tauri desktop app versions independently of the Python package (desktop/src-tauri/tauri.conf.json → version, currently 1.0.0), so it has its own workflow — .github/workflows/desktop-release.yml — driven by desktop-v* tags, not the PyPI v*.*.* tags above.

# Bump desktop/src-tauri/tauri.conf.json "version" first if needed
# (keep Cargo.toml / package.json in lockstep), then:
git tag desktop-v1.0.0
git push origin desktop-v1.0.0

The workflow (Windows / macOS / Linux matrix, fail-fast: false):

  1. Freezes the practical-tier sidecar in an isolated .venv-build (build-sidecar.{ps1,sh}) and runs evi-server --check.
  2. Builds the standalone app via tauri-action with --config src-tauri/tauri.standalone.conf.json (ships the onedir sidecar through bundle.resources).
  3. Creates a non-draft (published) GitHub release for the tag and attaches the minisign-signed installers (.msi/-setup.exe on Windows, .dmg/.app on macOS, .deb/.rpm/.AppImage on Linux) plus a generated latest.json for the in-app updater. Also uploads them as workflow artifacts, so a manual workflow_dispatch run (no tag) still produces downloadables.

Caveats:

In-app auto-update (Tauri updater)

The desktop app self-updates directly from the public evi-assistant/evi-ai GitHub releases. On launch the Rust shell checks releases/latest/download/latest.json, and if a newer signed build exists it downloads, installs, and restarts. Opt out with EVI_AUTO_UPDATE=0.

Docker

The CI workflow doesn't push images by default. To do it manually after a release:

docker build -t evi:0.8.0 .
docker tag evi:0.8.0 evi:latest
# Push to GHCR or Docker Hub from here.

The Dockerfile is set up for the headless web-server use case — pair it with docker-compose.yml for an Ollama + eVi stack out of the box.

Versioning

Loose semver:

What about breaking changes?

Until 1.0, breaking changes are allowed in minor versions. Call them out clearly in the CHANGELOG under a ### Breaking heading.

Roadmap

Edit on GitHub ↗