Containers and Release Automation
This chapter documents the PHIDS runtime container, local cleanup expectations, and the GitHub Actions workflows that publish container images and bundled binaries.
What Exists Now
PHIDS now has three release-related surfaces in addition to the main CI workflow:
Dockerfilefor the runtime image,docker-compose.ymlfor local containerized development,.github/workflows/docker-publish.ymlfor publishing a GHCR image,.github/workflows/release-binaries.ymlfor Linux/Windows/macOS bundled binaries.
These are intentionally separate from .github/workflows/ci.yml, which remains the merge-gating
quality workflow.
Local Container Run
Use Docker or a Docker-compatible Podman setup from the repository root:
docker compose up --build
This starts the FastAPI/HTMX application at http://127.0.0.1:8000/.
Current runtime behavior:
- the container executes
phids --host 0.0.0.0 --port 8000 --reload, ./srcis bind-mounted into/app/srcfor live reload,- the examples directory is mounted into
/app/examples, - the healthcheck probes
GET /instead of/api/simulation/statusbecause a fresh startup has no scenario loaded yet.
Local Cleanup After Container Work
When local container tests finish, remove the PHIDS-specific remnants:
docker rm -f phids-local
docker rmi -f phids:test phids:local
docker image prune -f
Why these commands:
phids-localis the named container created bydocker-compose.yml,phids:testandphids:localare the image tags used during local verification,docker image prune -fremoves dangling intermediate layers left by interrupted builds.
Do not use a broad docker system prune -a unless you explicitly want to clean unrelated
images and containers on your machine as well.
If you rehearse workflows locally with act, you may also want to remove the runner image that
act pulled for GitHub Actions emulation:
docker rmi ghcr.io/catthehacker/ubuntu:act-latest
That image is not required for normal PHIDS runtime/container usage; it is only needed for local GitHub Actions rehearsal.
Why Dependency Downloads Can Repeat
The repeated wheel downloads observed during testing do not necessarily mean the Dockerfile is wrong. There are multiple independent environments involved:
- your local development environment from
uv sync --all-extras --dev, - the builder environment created inside
docker build, - each GitHub Actions runner or matrix job,
- each operating system in the release-binary workflow.
Within Dockerfile, PHIDS deliberately uses two uv sync invocations:
uv sync --frozen --no-dev --no-install-project
uv sync --frozen --no-dev
The first command builds a cacheable dependency-only layer. The second command installs the project
itself after src/ has been copied. This avoids re-resolving the whole dependency graph when only
application code changes.
If the dependency layer build is interrupted before completion, the next build attempt must download those dependencies again because the prior layer never became reusable.
GitHub Container Publishing
The workflow .github/workflows/docker-publish.yml:
- logs in to
ghcr.iousing the repositoryGITHUB_TOKEN, - lowercases the repository name for a valid image path,
- builds a multi-architecture image for
linux/amd64andlinux/arm64, - publishes tags for release refs, semantic versions, and commit SHAs.
Typical resulting image names follow this pattern:
ghcr.io/<owner>/phids:latest
ghcr.io/<owner>/phids:<git-sha>
ghcr.io/<owner>/phids:v0.2.0
The workflow runs on:
- version tags matching
v*.*.*, - manual workflow dispatch.
That means ordinary branch pushes—including main and develop work—do not automatically publish
container images. Automatic GHCR publication now happens only for release tags.
Bundled Binary Publishing
The workflow .github/workflows/release-binaries.yml uses a GitHub Actions matrix over:
ubuntu-latest,windows-latest,macos-latest.
Each runner:
- installs the project dependencies with
uv, - builds a PyInstaller bundle from
packaging/phids.spec, - smoke-tests the launcher with
--help, - starts the bundled app and probes
GET /, - archives the resulting bundle,
- uploads the archive as a workflow artifact.
On version-tag runs (v*.*.*), the workflow also publishes those archives as GitHub release assets.
Packaging Inputs
The release bundle depends on:
src/phids/__main__.pyfor the executable entrypoint,pyproject.tomlfor thephidsconsole script,packaging/phids.specfor PyInstaller configuration,src/phids/api/templates/because the HTMX/Jinja UI must be present in the bundle,examples/andREADME.mdas bundled companion resources.
Recommended Release Flow
For a normal release:
git tag v0.2.0
git push origin v0.2.0
Expected behavior:
- the version tag publishes the GHCR image with release tags,
- the same version tag publishes the bundled archives to the GitHub release,
- a manual workflow dispatch is available if you need to rehearse the container build without cutting a tag.
Verification Notes
The container and release automation were validated with:
python -m phids --help,- focused API/UI route tests,
- workflow and packaging inspection,
- container-health smoke-check design targeting
GET /.
If a future change adds static assets under src/phids/api/static/, update both the runtime image
and packaging/phids.spec so those assets are included in the container and bundled binaries.