diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ae257bb6..d78c4ffd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -40,7 +40,10 @@ jobs: with: context: . platforms: linux/amd64,linux/arm64 - build-args: REVISION=${{ github.sha }} + # Only release builds carry a version; main builds report "dev". + build-args: | + REVISION=${{ github.sha }} + VERSION=${{ github.event_name == 'release' && github.event.release.tag_name || '' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} sbom: true @@ -49,10 +52,12 @@ jobs: - name: Smoke-run the published image env: IMAGE: ghcr.io/batchstream/weir@${{ steps.build.outputs.digest }} + TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || 'dev' }} run: | version=$(docker run --rm --pull=always --network=none "$IMAGE" version) echo "$version" test "$(jq -r .revision <<<"$version")" = "$GITHUB_SHA" + test "$(jq -r .version <<<"$version")" = "$TAG" archives: if: github.repository == 'batchstream/weir' && github.event_name == 'release' @@ -69,18 +74,26 @@ jobs: with: go-version-file: go.mod - name: Build archives + env: + TAG: ${{ github.event.release.tag_name }} run: | dist=$RUNNER_TEMP/dist for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64; do name=weir-${target%/*}-${target#*/} mkdir -p "$dist/$name/config" CGO_ENABLED=0 GOOS=${target%/*} GOARCH=${target#*/} go build -trimpath -buildvcs=false \ - -ldflags "-buildid= -X main.revision=$GITHUB_SHA" -o "$dist/$name/weir" ./cmd/weir + -ldflags "-buildid= -X main.revision=$GITHUB_SHA -X main.version=$TAG" -o "$dist/$name/weir" ./cmd/weir cp README.md LICENSE "$dist/$name/" cp config/weir.reference.yaml "$dist/$name/config/" cp -R docs "$dist/$name/docs" tar -czf "$dist/$name.tar.gz" --owner=0 --group=0 --numeric-owner -C "$dist/$name" weir README.md LICENSE config docs done + # The linker ignores a mistyped -X symbol, so check the identity a + # packaged binary reports. + identity=$("$dist/weir-linux-amd64/weir" version) + echo "$identity" + test "$(jq -r .version <<<"$identity")" = "$TAG" + test "$(jq -r .revision <<<"$identity")" = "$GITHUB_SHA" cd "$dist" && sha256sum weir-*.tar.gz > SHA256SUMS - name: Attach archives to the release env: diff --git a/Dockerfile b/Dockerfile index 0605ce16..8935dc12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,10 +4,10 @@ COPY go.mod go.sum ./ RUN go mod download COPY cmd cmd COPY internal internal -ARG TARGETOS TARGETARCH REVISION +ARG TARGETOS TARGETARCH REVISION VERSION RUN --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \ - go build -trimpath -ldflags "-buildid= -X main.revision=$REVISION" -o /weir ./cmd/weir + go build -trimpath -ldflags "-buildid= -X main.revision=$REVISION -X main.version=$VERSION" -o /weir ./cmd/weir FROM gcr.io/distroless/static-debian13@sha256:e2e927ec666bae08560abb3c55d0659eceabb657f56b6782ab500a9fc7f555e3 COPY LICENSE /LICENSE diff --git a/cmd/weir/main_test.go b/cmd/weir/main_test.go index 675f19b6..67d6a3c7 100644 --- a/cmd/weir/main_test.go +++ b/cmd/weir/main_test.go @@ -51,6 +51,25 @@ func TestVersionReportsInjectedRevision(t *testing.T) { } } +func TestVersionReportsInjectedVersion(t *testing.T) { + previous := version + version = "v9.8.7" + t.Cleanup(func() { version = previous }) + + var output bytes.Buffer + if err := run([]string{"version"}, &output); err != nil { + t.Fatal(err) + } + + var identity map[string]string + if err := json.Unmarshal(output.Bytes(), &identity); err != nil { + t.Fatal(err) + } + if identity["version"] != "v9.8.7" { + t.Fatal(identity) + } +} + func TestCheckConfigWithCredentialFilesWithoutBackendOrCAAccess(t *testing.T) { directory := t.TempDir() configDirectory := filepath.Join(directory, "nested") diff --git a/cmd/weir/version.go b/cmd/weir/version.go index f4746f37..a635f510 100644 --- a/cmd/weir/version.go +++ b/cmd/weir/version.go @@ -11,6 +11,10 @@ import ( // -X main.revision; other builds report the revision go build records. var revision string +// version is the release tag. Release builds set it with -X main.version; +// other builds report the module version go build records, or "dev". +var version string + type versionIdentity struct { Product string `json:"product"` Version string `json:"version"` @@ -29,7 +33,8 @@ func printVersion(output io.Writer) error { } if info, ok := debug.ReadBuildInfo(); ok { - // go build records module and VCS details when available; an injected revision wins. + // go build records module and VCS details when available; an injected + // revision or version wins. if info.Main.Version != "" && info.Main.Version != "(devel)" { identity.Version = info.Main.Version } @@ -40,5 +45,8 @@ func printVersion(output io.Writer) error { } } + if version != "" { + identity.Version = version + } return json.NewEncoder(output).Encode(identity) } diff --git a/docs/development.md b/docs/development.md index 60d30ff3..5c562bce 100644 --- a/docs/development.md +++ b/docs/development.md @@ -91,7 +91,8 @@ Every push to `main` publishes the image `ghcr.io/batchstream/weir:` for Linux amd64 and arm64. Publishing a GitHub release `vMAJOR.MINOR.PATCH` also publishes `ghcr.io/batchstream/weir:vMAJOR.MINOR.PATCH` and attaches Linux and macOS amd64/arm64 archives with their `SHA256SUMS`. `weir version` reports the -source commit as `revision`. Build the image locally with: +source commit as `revision`. Release images and archives report the release tag +as `version`; images from `main` report `dev`. Build the image locally with: ```sh docker buildx build --build-arg REVISION=$(git rev-parse HEAD) --load -t weir .