Skip to content

Versioning and release policy

Decided 2026-10-15. This binds releases, the images, the fix looper and every consumer of tacplus.

Versions

tacplus releases are MAJOR.MINOR.PATCH git tags vMAJOR.MINOR.PATCH (.github/workflows/release.yml builds the binaries from the tag).

  • A patch is fixes only. A new feature, a new annotation, a new generated surface or a changed generated surface never goes into a patch. A patch changes behaviour only where the previous behaviour was a defect.
  • Features come in on a minor. Anything a consumer would have to learn about lands in MAJOR.MINOR+1.0.
  • A major is a breaking change to what consumers already generate against. Generated code is regenerated by its consumers; there is no deprecation window (a window is hand-maintained compatibility). The major says so.

What consumers pin

A consumer pins major and minor, never patch. 2.1 means "the newest 2.1.x", and every 2.1.x must be safe to take without reading the notes, because a patch is fixes only. Pinning a patch is allowed for reproducing a problem, never for staying on a known defect.

Backports

Every fix is made on main first. It is then backported to every minor released within the last twelve months, as its own patch release of that minor. A minor older than a year is out of support: it gets no patches, and a consumer still on it upgrades.

Each supported minor has a branch release/MAJOR.MINOR, cut at its .0 tag (or at its first good patch). A patch release is a tag on that branch. The branch is protected: changes land only by pull request.

Supported today: release/2.0 (cut at v2.0.1; 2.0.0 published images but its release build failed, so 2.0.1 is the first usable 2.0 release). Consumers pin 2.0: the tacplus:2.0 image and tacplus upgrade both follow its newest patch. The fix looper opens one pull request against main and one against each supported release branch; the release-branch pull request carries only the fix and its test.

Images

Three images. build is FROM base; tacplus copies its tools out of build onto a slim OS, so a consumer pulls a few hundred megabytes, not the SDKs:

image contents rebuilt when
ghcr.io/tacplus-dev/tacplus/base OS and every SDK tacplus is built and tested with: Go, Java (JDK, Maven), Rust, Python, Node (TypeScript), Swift, .NET on a schedule, or when an SDK pin changes
ghcr.io/tacplus-dev/tacplus/build base plus every tool: protoc and every plugin, grpcio-tools, JBMC and OpenJML, just, the docker CLI, gcloud. The one image the gates run in: CI, the nightly gates, just gates-container. Not for consumers. when a tool pin changes
ghcr.io/tacplus-dev/tacplus/tacplus the tacplus binary and the protoc plugins for every language that needs its own (Go, Java, TypeScript, Rust, Swift, Python); C# needs none, its generated project runs Grpc.Tools from NuGet every push to main and every release

A consumer's build does not start FROM the tacplus image. It runs it as its own step (tacplus build, writing the generated model) and compiles the result in its own builder image, so a new tacplus patch is picked up by the generate step pulling tacplus:X.Y again, without rebuilding the consumer's image.

Tags on the tacplus image follow the version policy:

tag meaning
2.1.3 that release, immutable
2.1 the newest 2.1.x patch (what a consumer pins)
2 the newest 2.x
latest the newest release
main-<sha> an unreleased build of main

base and build are tagged <yyyymmdd>-<sha> and latest.

The definitions are images/base.Dockerfile, images/build.Dockerfile and images/tacplus.Dockerfile; images/smoke.sh ships in the build and tacplus images as tacplus-smoke and runs inside every pushed image. .github/workflows/images.yml builds and pushes them for linux/amd64 and linux/arm64 on every push to main and every release tag, rebuilding base and build only when their file changed (or weekly for base, or on demand). just images builds the set natively for one machine. The first release tag is v2.0.0: the public tacplus-dev/tacplus line is a new major over the private v1.2.x line it was forked from (new module path, regenerated consumers).