From 16481e67d0deda865eb680f4fddaf791641bae5c Mon Sep 17 00:00:00 2001 From: Jay Ravani Date: Sat, 5 Sep 2026 22:21:22 +0200 Subject: [PATCH] build: stamp the version into the published Docker images `ignis -version` in the ghcr.io/thd-spatial-ai/ignis image reported "dev (commit none, built unknown)" because the image build never passed the -ldflags -X that release.yml uses for the standalone binaries. A deployed container could not say which release it was running. - Both environment/*.dockerfile take VERSION, COMMIT and DATE build args (defaulting to the same values internal/version hard-codes, so a plain `docker build` and the dev compose path are unchanged) and pass them to `go build` as -ldflags -X, matching release.yml. - docker-publish.yml passes those args: VERSION from the image tag (metadata-action's {{version}}, e.g. 0.5.0), COMMIT from github.sha, DATE from a build-time timestamp. - The version-line format moves from cmd/ignis into version.String() so build_db can print it in its startup banner too; its test moves to internal/version with it. Verified locally: a stamped app image reports the real version, a stamped build_db logs it in its banner, an unstamped build still reports the hard-coded defaults. Claude-Session: https://claude.ai/code/session_01JBfY6ZG2W52imgy2M1JGYo --- .github/workflows/docker-publish.yml | 11 +++++++++++ cmd/build_db/main.go | 2 ++ cmd/ignis/main.go | 6 +----- cmd/ignis/main_test.go | 12 ------------ environment/ignis-app.dockerfile | 13 ++++++++++++- environment/ignis-db.dockerfile | 12 +++++++++++- internal/version/version.go | 10 ++++++++++ internal/version/version_test.go | 15 +++++++++++++++ 8 files changed, 62 insertions(+), 19 deletions(-) create mode 100644 internal/version/version_test.go diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 4da4ba7..84bf372 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -46,12 +46,23 @@ jobs: type=sha,format=short flavor: latest=auto + - name: Build date + id: date + run: echo "value=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ${{ matrix.dockerfile }} push: true + # Stamps internal/version so `ignis -version` reports the release. + # meta.version is the tag without the leading v (0.5.0), matching + # the published image tag. + build-args: | + VERSION=${{ steps.meta.outputs.version }} + COMMIT=${{ github.sha }} + DATE=${{ steps.date.outputs.value }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha,scope=${{ matrix.name }} diff --git a/cmd/build_db/main.go b/cmd/build_db/main.go index 8f67023..3909a90 100644 --- a/cmd/build_db/main.go +++ b/cmd/build_db/main.go @@ -6,6 +6,7 @@ import ( "github.com/thd-spatial-ai/ignis/internal/config" importer "github.com/thd-spatial-ai/ignis/internal/db" "github.com/thd-spatial-ai/ignis/internal/utils" + "github.com/thd-spatial-ai/ignis/internal/version" "log" "time" @@ -16,6 +17,7 @@ func main() { startTime := time.Now() fmt.Println("============================================================") fmt.Println("=== ignis Database Rebuild Tool ===") + fmt.Printf("=== %s\n", version.String()) fmt.Println("============================================================") fmt.Println("") diff --git a/cmd/ignis/main.go b/cmd/ignis/main.go index 260ed23..a4a7222 100644 --- a/cmd/ignis/main.go +++ b/cmd/ignis/main.go @@ -11,10 +11,6 @@ import ( "github.com/thd-spatial-ai/ignis/internal/version" ) -func versionString() string { - return fmt.Sprintf("%s (commit %s, built %s)", version.Version, version.Commit, version.Date) -} - // parseVersionFlag reports whether args request the version string, so the // flag-handling decision can be tested without starting a server or exiting. func parseVersionFlag(args []string) bool { @@ -28,7 +24,7 @@ func parseVersionFlag(args []string) bool { // Setup app server and routes func main() { if parseVersionFlag(os.Args[1:]) { - fmt.Println(versionString()) + fmt.Println(version.String()) os.Exit(0) } diff --git a/cmd/ignis/main_test.go b/cmd/ignis/main_test.go index 7f57ee6..d85d794 100644 --- a/cmd/ignis/main_test.go +++ b/cmd/ignis/main_test.go @@ -1,21 +1,9 @@ package main import ( - "strings" "testing" - - "github.com/thd-spatial-ai/ignis/internal/version" ) -func TestVersionString(t *testing.T) { - got := versionString() - for _, want := range []string{version.Version, version.Commit, version.Date} { - if !strings.Contains(got, want) { - t.Errorf("versionString() = %q, want it to contain %q", got, want) - } - } -} - func TestParseVersionFlag(t *testing.T) { cases := map[string][]string{ "no flags": {}, diff --git a/environment/ignis-app.dockerfile b/environment/ignis-app.dockerfile index 6eb8c3e..08681c6 100644 --- a/environment/ignis-app.dockerfile +++ b/environment/ignis-app.dockerfile @@ -13,10 +13,21 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . +# Passed by the publish workflow on a tag build; default to the same values +# internal/version hard-codes, so a plain `docker build` is unchanged. +ARG VERSION=dev +ARG COMMIT=none +ARG DATE=unknown # -buildvcs=false: the build context may contain .git, and the toolchain's # VCS stamping shells out to git, which fails on an ownership mismatch in CI. # The image is identified by its release tag, so the stamp buys nothing. -RUN CGO_ENABLED=0 go build -buildvcs=false -o /out/ignis ./cmd/ignis +# -X: same three variables release.yml stamps into the standalone binaries. +RUN CGO_ENABLED=0 go build -buildvcs=false \ + -ldflags="-s -w \ + -X github.com/thd-spatial-ai/ignis/internal/version.Version=${VERSION} \ + -X github.com/thd-spatial-ai/ignis/internal/version.Commit=${COMMIT} \ + -X github.com/thd-spatial-ai/ignis/internal/version.Date=${DATE}" \ + -o /out/ignis ./cmd/ignis # --------------------------------------------------------------------------- # Stage 2: final — only the compiled server binary, no compiler/source/git diff --git a/environment/ignis-db.dockerfile b/environment/ignis-db.dockerfile index 4f38508..3bfbf05 100644 --- a/environment/ignis-db.dockerfile +++ b/environment/ignis-db.dockerfile @@ -17,7 +17,17 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN CGO_ENABLED=0 go build -buildvcs=false -o /out/build_db ./cmd/build_db +# Passed by the publish workflow on a tag build; default to the same values +# internal/version hard-codes, so a plain `docker build` is unchanged. +ARG VERSION=dev +ARG COMMIT=none +ARG DATE=unknown +RUN CGO_ENABLED=0 go build -buildvcs=false \ + -ldflags="-s -w \ + -X github.com/thd-spatial-ai/ignis/internal/version.Version=${VERSION} \ + -X github.com/thd-spatial-ai/ignis/internal/version.Commit=${COMMIT} \ + -X github.com/thd-spatial-ai/ignis/internal/version.Date=${DATE}" \ + -o /out/build_db ./cmd/build_db # --------------------------------------------------------------------------- # Stage 2: final — compiled binary + the trimmed TABULA workbook baked in. diff --git a/internal/version/version.go b/internal/version/version.go index 155cee9..803b0b0 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,7 +1,17 @@ package version +import "fmt" + var ( Version = "dev" Commit = "none" Date = "unknown" ) + +// String renders the build's version, commit and date on one line. +// The three vars are set at link time by -ldflags -X (see release.yml and +// the environment/*.dockerfile builds); an unstamped build reports the +// hard-coded defaults. +func String() string { + return fmt.Sprintf("%s (commit %s, built %s)", Version, Commit, Date) +} diff --git a/internal/version/version_test.go b/internal/version/version_test.go new file mode 100644 index 0000000..eba6902 --- /dev/null +++ b/internal/version/version_test.go @@ -0,0 +1,15 @@ +package version + +import ( + "strings" + "testing" +) + +func TestString(t *testing.T) { + got := String() + for _, want := range []string{Version, Commit, Date} { + if !strings.Contains(got, want) { + t.Errorf("String() = %q, want it to contain %q", got, want) + } + } +}