Packaging and Releasing TAU Commander¶
TAU Commander is a standard Python package, built with setuptools from pyproject.toml and published to
PyPI as taucmdr. It is installed from PyPI it with uv tool install taucmdr or pipx install taucmdr
(see Installing TAU Commander).
Versions¶
setuptools-scm takes the version from git tags, so there’s no version number to edit before a release. A
commit tagged v1.7.0 builds as 1.7.0. Any other commit builds as a development version of the next
patch release: five commits after v1.7.0, the version is 1.7.1.dev5+g<commit hash>. If tracked files
have uncommitted changes, the version also includes a date, e.g. 1.7.1.dev5+g<commit hash>.d20261029.
tau --version shows the current version.
Release tags are v followed by the version, e.g. v1.7.0rc1 for a release candidate and v1.7.0
for the final release. Always make annotated tags (git tag -a). When a release candidate and the release
are on the same commit, setuptools-scm picks the newer annotated tag; a lightweight v1.7.0 beside an
annotated v1.7.0rc1 would still builds as 1.7.0rc1.
A given version number can only be uploaded to PyPI once, even if the files are deleted later, thus, a mistake in a release necessitates a new version number.
Building¶
Build the sdist and the wheel with:
uv build
Both are written to dist/. The wheel is pure Python and holds the taucmdr package, including the vendored
TinyDB in taucmdr/_vendor/tinydb, and the tau launcher in taucmdr-<version>.data/scripts/. It
installs no other top-level package. The sdist includes most tracked files, including docs/, examples/
and .testfiles/, but leaves out the CI and tool configuration, such as .github/ and
.pre-commit-config.yaml.
Why TAU Commander runs in its own environment¶
TAU Commander profiles Python programs, so the user’s Python packages and TAU Commander’s own must never
mix. If the user’s environment had a package named tau, for example, both TAU Commander and the user’s
program could import the wrong one. That’s why TAU Commander is only installed with uv tool or pipx,
which give it a virtual environment of its own, and never with pip into an environment the user directly
works with.
A separate environment doesn’t stop the user’s PYTHON* environment variables from reaching TAU
Commander’s interpreter. PYTHONPATH or PYTHONHOME could still change which modules it imports, or
stop it from starting. The tau command is therefore a POSIX sh script (scripts/tau) rather than a
script entry point. It finds the Python interpreter in its own environment and runs python -I -m taucmdr.
Isolated mode (-I) makes that interpreter ignore every PYTHON* variable and the per-user
site-packages directory. Though inert, the variables nonetheless remain in the environment, so programs run by
TAU Commander continue to see them, so that a profiled Python interpreter will still inherit and honor these variables
even though the interpreter running TAU Commmander ignores them. If taucmdr starts without isolated mode, for example from
python -m taucmdr, taucmdr/__main__.py runs itself again with -I.
Testing a build before publishing¶
There are three ways to get a wheel to try out before publishing to TestPyPI:
uv buildin a checkout, as above. Tag the commit locally first (git tag -a v1.7.0rc1) to build a release version.Run the release workflow locally with
gh act, which also runs the workflow’s checks. The workflow skips its publishing steps under act. With a local tag on the commit:echo '{"ref": "refs/tags/v1.7.0rc1"}' > event.json gh act push -W .github/workflows/release.yml -e event.json \ -P ubuntu-24.04=catthehacker/ubuntu:act-24.04 --artifact-server-path /tmp/act-artifacts
The wheel and sdist are in
/tmp/act-artifacts/1/dist/dist.zip.Due to a bug in act v0.2.89’s, the artifact server rejects
actions/upload-artifact@v7; until that’s fixed, add--local-repository actions/upload-artifact@v7=<checkout of actions/upload-artifact at v4>.Run the release workflow on GitHub on a branch. A manual run builds and checks the distributions but does not publish them:
gh workflow run release.yml --ref <branch> gh run download <run ID> -n dist
A branch builds a development version, which PyPI would reject if an attempt was made to publish it.
Releasing¶
Make sure CI passes on the branch, both on push and in a manual run on the older Pythons:
gh workflow run CI.yml --ref <branch> -f python-versions='["3.11", "3.12", "3.13", "3.14"]'
Merge the branch into
master.Tag a release candidate on
masterand push the tag:git tag -a v1.7.0rc1 -m "TAU Commander 1.7.0rc1" git push origin v1.7.0rc1
The release workflow (
.github/workflows/release.yml) builds the sdist and wheel, checks them, and publishes tags withrcin them to TestPyPI only.Install the release candidate and try it. Take only TAU Commander’s wheel from TestPyPI, so its dependencies come from PyPI rather than from whatever uploads TestPyPI has under the same names:
python3 -m pip download --no-deps --index-url https://test.pypi.org/simple/ --dest rc taucmdr==1.7.0rc1 uv tool install rc/taucmdr-1.7.0rc1-py3-none-any.whl
Tag the release on the same commit, or on a later one if the candidate needed fixes, and push the tag:
git tag -a v1.7.0 -m "TAU Commander 1.7.0" git push origin v1.7.0
Tags without
rcare published to PyPI.
Setting up publishing¶
The release workflow publishes with PyPI’s trusted publishing, so no PyPI password or API token is stored in GitHub. Each publish job asks GitHub for a short-lived token that indicates the repository, workflow and environment in which it runs. PyPI accepts the upload only if those match a publisher registered for the project. This setup is needed once per site.
On PyPI, add a publisher for the
taucmdrproject (a pending publisher, before the first release creates the project) with ownerParaToolsInc, repositorytaucmdr, workflowrelease.ymland environmentpypi.On TestPyPI, which has its own accounts, do the same with environment
testpypi.In the GitHub repository’s settings, create the environments
testpypiandpypi. Limit both to tags matchingv*, so a workflow run from a branch can’t publish, and add required reviewers topypiso each release waits for an approval.