This document describes the versioning convention used across every GrayCodeAI repository that follows this layout. Adopted 2026-05-14.
- Every repo has a
VERSIONfile at the root (plain text, e.g.0.2.0). VERSIONis the single source of truth — everything else (code, build tooling, release tooling, CI, package metadata) reads from it.- Versions are independent per repo, following SemVer.
VERSIONat the repo root.- A version package (
internal/versionfor binaries, ormainitself) declares three settable variables defaulting to"dev"/"none"/"unknown":var ( Version = "dev" // overridden by ldflags at release time Commit = "none" Date = "unknown" )
- The
MakefilereadsVERSIONand injects all three via ldflags:VERSION := $(shell cat VERSION 2>/dev/null | tr -d '[:space:]' || echo "dev") COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") DATE := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ') LDFLAGS := -s -w \ -X main.Version=$(VERSION) \ -X main.Commit=$(COMMIT) \ -X main.BuildDate=$(DATE)
goreleaserinjects from the git tag at release time (which always matches theVERSIONfile because release-please bumps both atomically).- This is the Kubernetes / Helm / gh-cli pattern.
VERSIONat the repo root.- A
version.gofile co-located withVERSIONuses//go:embedto read it at compile time:package mylib import ( _ "embed" "strings" ) //go:embed VERSION var versionFile string var Version = strings.TrimSpace(versionFile)
- This is the AWS SDK / restic / hashicorp pattern. It works without ldflags,
so consumers who
go installorgo getget the correct version automatically.
When an internal sub-package (e.g. internal/output, internal/report) needs
the version too, it declares its own var ToolVersion = "dev" plus a
SetToolVersion(v string) setter. The parent package's init() propagates
the canonical Version into the sub-package, avoiding an import cycle:
// in <root>/version.go
import "github.com/<org>/<repo>/internal/output"
func init() {
output.SetToolVersion(Version)
}VERSIONat the repo root.pyproject.tomlusesdynamic = ["version"]with hatch reading fromVERSION:[project] dynamic = ["version"] [tool.hatch.version] source = "regex" path = "VERSION" pattern = "^(?P<version>[^\\s]+)" [tool.hatch.build.targets.wheel] force-include = { "VERSION" = "hawk/VERSION" }
_version.pyreads the sameVERSIONfile at runtime viapathlib, so__version__matches the package metadata both in source checkouts and in installed wheels (whereVERSIONships as package data).
- Hardcoding a version in source code (e.g.
const Version = "0.2.0"). Always read fromVERSIONor a build-time injection. - Maintaining a version string in two places (e.g.
pyproject.tomlAND_version.py) — they drift, always. - Embedding hardcoded version literals in tests. Compare against the package
variable instead:
if got != mypkg.Version { ... }.
Use release-please (per repo).
It reads conventional commits, bumps VERSION, updates CHANGELOG.md, and
creates a git tag — all atomically. Goreleaser then runs on the tag.
For manual bumps, edit the VERSION file. Don't edit anything else.
Versions are independent per repo. A future compatibility-matrix.json (at
the eco root) will record which combinations are tested together (similar to
the AWS SDK / Hashicorp release matrices).