Release Process¶
This page describes how to manage versioning, build and publish Python packages to PyPI, build and publish Docker images, and deploy documentation — all through GitHub Actions.
Overview¶
The release pipeline consists of the following coordinated steps:
0. Update changelog (docs/changelog.md)
↓
1. Create a git tag (vX.Y.Z)
↓
2. Build & publish packages to PyPI
↓
3. Build & publish Docker images
↓
4. Deploy documentation
All workflows are defined in .github/workflows/.
0. Updating the Changelog¶
Before cutting a release tag, update docs/changelog.md with all issues from the associated GitHub project.
Prerequisites¶
- GitHub CLI (
gh) installed. - Authenticated via
gh auth login. - Python dependencies installed (
pandas).
Steps¶
-
Identify the GitHub project number for the release. For example, project 83 tracks the
3.1.0milestone.
Find it in the project URL:https://github.com/orgs/InstituteforDiseaseModeling/projects/<number>. -
Run the script from the
dev_scripts/directory: -
Review the updated
docs/changelog.mdto verify entries look correct, then commit:
1. Versioning¶
How versions work¶
All packages use setuptools-scm for fully dynamic versioning. There is no static version stored in any pyproject.toml — every package declares dynamic = ["version"] and the version is computed at build time directly from the git tag:
[project]
dynamic = ["version"]
[tool.setuptools_scm]
root = ".."
version_scheme = "no-guess-dev"
local_scheme = "no-local-version"
fallback_version = "0.0.0.dev0"
- Tag format:
v{MAJOR}.{MINOR}.{PATCH}(e.g.,v3.0.6) - Fallback (no tag reachable):
0.0.0.dev0 no-local-version: strips the+gHASHlocal suffix so published wheels have clean version strings
Version between tags¶
We only update tags with releases. Between releases, setuptools-scm automatically appends a post-release and dev segment to reflect the number of commits since the last tag:
For example, 4 commits after tagging v3.0.6:
idmtools: 3.0.6.post1.dev4
idmtools_cli: 3.0.6.post1.dev4
idmtools_platform_comps: 3.0.6.post1.dev5
idmtools_models: 3.0.6.post1.dev5
idmtools_platform_general: 3.0.6.post1.dev5
idmtools_platform_slurm: 3.0.6.post1.dev6
idmtools_platform_container: 3.0.6.post1.dev5
idmtools_test: 3.0.6.post1.dev5
The dev number varies per package because each package's version is computed independently based on how many commits have touched files under its subdirectory since the last tag.
You can check the current version of all installed packages at any time:
The built wheel filename and package metadata reflect whatever setuptools-scm resolves at python -m build time. No file editing is needed to change the version; creating a new git tag is the only required action.
Creating a new version¶
To release a new version, create and push an annotated git tag. That is the only step required — setuptools-scm picks it up automatically at build time.
Pushing a v* tag triggers the Deploy Packages workflow, which builds all packages and publishes them to PyPI (after manual approval).
How the version flows into built packages
When python -m build runs, setuptools-scm walks the git history, finds the nearest v* tag, and injects that value as the package version. No file editing is needed.
2. Building and Publishing Python Packages¶
Packages in this repository¶
The monorepo contains 8 packages, all built in parallel:
| Package directory | PyPI name |
|---|---|
idmtools_core | idmtools |
idmtools_cli | idmtools-cli |
idmtools_models | idmtools-models |
idmtools_platform_comps | idmtools-platform-comps |
idmtools_platform_general | idmtools-platform-general |
idmtools_platform_slurm | idmtools-platform-slurm |
idmtools_platform_container | idmtools-platform-container |
idmtools_test | idmtools-test |
Workflow: Deploy Packages (deploy.yml)¶
Triggers:
| Event | What happens |
|---|---|
Push to main branch | Builds packages only (no upload) |
Tag matching v* | Builds packages → publishes to PyPI (after approval) → builds Docker images |
Manual dispatch with target=test | Builds + publishes to TestPyPI + builds Docker staging image |
Each step is explained below. No manual intervention is required beyond the trigger events mentioned above.
Step 1 — Build all packages¶
Each package is built using the PEP 517 build frontend:
This produces both a source distribution (.tar.gz) and a wheel (.whl) in idmtools_{package}/dist/. Built artifacts are uploaded as GitHub Actions artifacts with a 1-day retention.
Step 2 — Publish to TestPyPI (manual, optional)¶
Run the Deploy Packages workflow manually from with target=test:
- Go to Actions → Deploy Packages.
- Click Run workflow, select a branch you want to build from, set Where to upload to
test. - Packages are uploaded to TestPyPI using the
TEST_PYPI_API_TOKENsecret. skip-existing: trueprevents errors if the version already exists.
Install from TestPyPI to verify:
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ idmtools==3.x.x
Step 3 — Publish to production PyPI (automatic on tag)¶
When a v* tag is pushed (created by the git tag):
- All 8 packages are built.
- The
publish-to-pypijob waits for a manual approval from reviewers in thedeploy_pypiGitHub environment. - After approval, packages are published using OIDC Trusted Publishers — no API token needed.
Manual approval required
Production PyPI publishing requires a reviewer to approve the deploy_pypi environment gate in the GitHub Actions UI before the upload proceeds.
Local build and staging upload¶
# Build all packages locally
make dist
# Upload to test pypi
twine upload dist/* --repository-url https://test.pypi.org/legacy/
3. Building and Publishing Docker Images¶
Two Docker images are maintained in this repository, both published to the GitHub Container Registry (GHCR).
Image 1 — SSMT Worker Image (COMPS platform)¶
Registry path: ghcr.io/institutefordiseasemodeling/idmtools_comps_ssmt_worker
Build script: idmtools_platform_comps/ssmt_image/build_ssmt_image.py
Staging image¶
Trigger: Manual dispatch via Deploy Packages with target=test, or via the separate Build SSMT Image Staging workflow (build_ssmt_image_staging.yml).
The workflow:
- Installs
idmtools_platform_compsfrom the build artifacts. - Logs into GHCR using
GITHUB_TOKEN. - Runs the build script to build and push a staging image:
Production image¶
Trigger: Automatically after a v* tag is pushed and all packages are published to PyPI.
The build script:
- Queries GHCR to determine the next build number automatically.
- Tags the image with three tags:
- Full version:
3.1.0.5 - Base version:
3.1.0 latest
- Full version:
Local build (without push)¶
To build and push manually:
Image 2 — Container Platform Runtime Image¶
Registry path: ghcr.io/institutefordiseasemodeling/container-rocky-runtime
Build script: idmtools_platform_container/docker_image/build_container_image.py
Trigger: Manual dispatch only via the Build Container Image workflow (build_container_image.yml).
The workflow runs:
The script:
- Reads
BASE_VERSIONfile for the base version string. - Queries GHCR to calculate the next build number automatically.
- Builds using the
Dockerfilein the same directory. - Tags and pushes to GHCR.
To build without pushing:
GHCR authentication (local builds)¶
The token requires write:packages and read:packages scopes.
4. Building and Deploying Documentation¶
Documentation is built with MkDocs and the Material theme, then deployed to GitHub Pages.
Live site: https://institutefordiseasemodeling.github.io/idmtools/
Workflow: Deploy MkDocs (deploy_docs_api.yml)¶
Triggers:
| Event | What happens |
|---|---|
Push to main branch | Builds and deploys docs automatically |
| Manual dispatch | Builds and deploys on demand |
Building docs locally¶
# Install documentation dependencies
pip install -r docs/requirements.txt
# Build once
mkdocs build
# Serve locally with auto-reload at http://localhost:8000
mkdocs serve
Concurrency protection
The workflow uses a pages concurrency group with cancel-in-progress: false to prevent partial deployments from being cancelled.
Documentation dependencies¶
From docs/requirements.txt:
mkdocs>=1.6.0
mkdocs-material>=9.5.0
pymdown-extensions>=10.7.0
mkdocs-minify-plugin>=0.8.0
5. Full Release Checklist¶
Use this checklist when cutting a new release:
- All changes merged to
devbranch and tests passing. - Create new release tag with
git tag vx.y.zand push to repo withgit push origin vx.y.z - Verify the new tag (e.g.,
v3.1.0) appears in the repository. - Merge
dev→mainvia pull request. - The push to
maintriggers:- Deploy Packages — builds all 8 packages.
- Deploy MkDocs — builds and deploys documentation to GitHub Pages.
- To publish to TestPyPI (optional validation step before release):
- Run Actions → Deploy Packages manually on
any branchwithtarget=test. - Install from TestPyPI and smoke-test.
- Run Actions → Deploy Packages manually on
- Once confident, the
v*tag triggers production PyPI publish — approve thedeploy_pypienvironment gate when prompted. - After PyPI publish succeeds, the SSMT Docker image is automatically built and pushed to GHCR.
- To rebuild the Container Runtime image separately: run Actions → Build Container Image manually.
6. Required Secrets and Environments¶
| Secret / Environment | Used by | Purpose |
|---|---|---|
TEST_PYPI_API_TOKEN | deploy.yml | Upload to TestPyPI |
GITHUB_TOKEN | deploy.yml, deploy_docs_api.yml, build_ssmt_image_staging.yml | GHCR login, Pages deploy (auto-provided by GitHub) |
deploy_pypi environment | deploy.yml | Manual approval gate for production PyPI publish |