Releases and images¶
A release is a git tag vX.Y.Z. Pushing one runs
.github/workflows/release.yml, which publishes three images and a GitHub
release. Nothing else publishes images: a push to main builds none.
What a tag publishes¶
| Image | Platforms | Built from |
|---|---|---|
silentswarm-leader |
linux/amd64, linux/arm64 |
docker/Dockerfile.leader |
silentswarm-frontend |
linux/amd64, linux/arm64 |
docker/Dockerfile.frontend |
silentswarm-fellow |
linux/amd64 |
docker/Dockerfile.fellow (torch wheels with CUDA) |
The leader and frontend also build for arm64 because a leader is a small box: a Raspberry Pi or an Ampere VM is enough. The fellow is amd64 only, where the CUDA wheels are.
Each image is tagged X.Y.Z, X.Y and latest, under
ghcr.io/<repository owner>/ unless the repository variables IMAGE_REGISTRY
and IMAGE_NAMESPACE name another place (a registry other than GHCR also needs
the secret IMAGE_REGISTRY_TOKEN). Every image carries an SBOM and a
provenance attestation (buildx --sbom --provenance=mode=max); inspect them
with docker buildx imagetools inspect <image> --format '{{ json .SBOM }}'.
Whether a package is public is a setting on the package in GitHub, not in the
workflow.
The version is baked in as SWARM_IMAGE_VERSION. The leader reports it in
GET /api/v1/health ("version") and in the footer of the signed-in pages,
and a fellow reports it at registration, so the Nodes view shows which release
each machine runs. A local build leaves it empty and the installed package's
version stands in.
Cutting a release¶
- Set
version = "X.Y.Z"inpyproject.tomlandappVersion: "X.Y.Z"indeploy/helm/silentswarm/Chart.yaml(the images the chart installs by default). Bump the chart's ownversiontoo when the chart changed. - In
CHANGELOG.md, move the entries under## [Unreleased]to a new## [X.Y.Z] - YYYY-MM-DDsection. It becomes the release notes. - Commit, then tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z.
Before tagging, check the keyless MCP sign-in with the current TypeScript MCP SDK, which most MCP clients build on (the Python SDK runs in the test suite):
npm i --prefix /tmp/mcp-sdk @modelcontextprotocol/sdk@latest
MCP_SDK_DIR=/tmp/mcp-sdk node scripts/mcp_oauth_client_check.mjs
It signs in to a throwaway leader as a client would, calls tools and
refreshes, for /mcp and a toolset URL. Put the SDK version it prints, and
any client apps you signed in with by hand, in the client table of
docs/06_user_guide/mcp.md ("Which clients are checked").
Check steps 1 and 2 before tagging with
python3 scripts/release_notes.py vX.Y.Z: it prints the notes, or says what
disagrees. The workflow runs the same check first and builds nothing when it
fails.
What the workflow checks before it pushes¶
Each image is first built for amd64 and loaded into the runner, and
scripts/smoke_image.sh runs against it:
- leader:
/api/v1/healthanswers, with the tag's version; - frontend:
/docs/is served, with the canonical URL written at start, by a process that is not root; - fellow:
import torchworks andfellow --helpruns.
Only then is the multi-platform image built and pushed. The same script works on a local build:
docker build -f docker/Dockerfile.leader --build-arg SWARM_IMAGE_VERSION=0.0.0 -t leader:try .
scripts/smoke_image.sh leader leader:try 0.0.0