fix(build): embed commit SHA for release provenance (#1571 part 2) (#1590)

* fix(build): embed commit SHA for release provenance (#1571 part 2)

Docker builds exclude .git via .dockerignore, so buildvcs cannot read
VCS metadata and published images carry no commit information. A running
image cannot be lined up with the source commit it was built from.

Embed cmd.CommitSHA at link time (maintainer-endorsed option 2) and
surface it in goclaw version output:

- cmd: add CommitSHA var, print commit in version cmd when injected
- Makefile: pass git rev-parse HEAD via LDFLAGS
- Dockerfile: accept COMMIT_SHA build arg (default unknown)
- docker-compose.yml: pass GOCLAW_COMMIT_SHA through as build arg
- release workflows: inject github.sha into release and dev-beta builds

Backward compatible: binaries built without the flag keep the existing
version output format.

* fix(build): pass COMMIT_SHA to docker image builds, surface it in doctor/upgrade

Review follow-up for PR #1590:

- Add COMMIT_SHA=${{ github.sha }} to the build-args of every
  docker/build-push-action step (release.yaml, dev-beta-release.yaml,
  release-beta.yaml, fork-image.yaml) so published images — the
  artifact issue #1571 is about — carry the commit, not just release
  tarball binaries.
- Surface the commit in doctor and upgrade output (App version line)
  via a shared commitSuffix() helper, so operators can read provenance
  from logs without exec'ing goclaw version (review suggestion #2).
- version cmd refactored onto the same helper; output unchanged.
This commit is contained in:
fchengyan authored and GitHub committed 2026-09-30 15:38:53 +07:00
1 parent 3f90057c24
commit 52ced3710f
10 files changed
+32 -9

No files matched your search

+3 -2
View File
@@ -149,7 +149,7 @@ jobs:
VERSION: ${{ needs.beta_version.outputs.tag }}
run: |
CGO_ENABLED=0 go build -tags embedui \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION} -X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=${{ github.sha }}" \
-o goclaw .
tar -czf "goclaw-${VERSION}-linux-amd64.tar.gz" goclaw migrations/ skills/
@@ -197,7 +197,7 @@ jobs:
VERSION: ${{ needs.beta_version.outputs.tag }}
run: |
CGO_ENABLED=0 go build -tags embedui \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION} -X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=${{ github.sha }}" \
-o goclaw .
tar -czf "goclaw-${VERSION}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" goclaw migrations/ skills/
@@ -352,6 +352,7 @@ jobs:
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
ENABLE_MEDIA_PROBES=${{ matrix.enable_media_probes }}
VERSION=${{ needs.beta_version.outputs.tag }}
COMMIT_SHA=${{ github.sha }}
cache-from: type=gha,scope=dev-beta-${{ matrix.variant }}
cache-to: type=gha,mode=max,scope=dev-beta-${{ matrix.variant }}
+1
View File
@@ -49,5 +49,6 @@ jobs:
ENABLE_OTEL=false
ENABLE_FULL_SKILLS=false
VERSION=fork-dev
COMMIT_SHA=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
+1
View File
@@ -153,5 +153,6 @@ jobs:
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
ENABLE_MEDIA_PROBES=${{ matrix.enable_media_probes }}
VERSION=${{ github.ref_name }}
COMMIT_SHA=${{ github.sha }}
cache-from: type=gha,scope=beta-${{ matrix.variant }}
cache-to: type=gha,mode=max,scope=beta-${{ matrix.variant }}
+2 -1
View File
@@ -104,7 +104,7 @@ jobs:
VERSION: v${{ needs.release.outputs.version }}
run: |
CGO_ENABLED=0 go build -tags embedui \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION} -X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=${{ github.sha }}" \
-o goclaw .
tar -czf "goclaw-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" goclaw migrations/ skills/
@@ -228,6 +228,7 @@ jobs:
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
ENABLE_MEDIA_PROBES=${{ matrix.enable_media_probes }}
VERSION=v${{ needs.release.outputs.version }}
COMMIT_SHA=${{ github.sha }}
cache-from: type=gha,scope=release-${{ matrix.variant }}
cache-to: type=gha,mode=max,scope=release-${{ matrix.variant }}
provenance: false
+3 -1
View File
@@ -41,6 +41,7 @@ ARG ENABLE_TSNET=false
ARG ENABLE_REDIS=false
ARG ENABLE_EMBEDUI=false
ARG VERSION=
ARG COMMIT_SHA=
# Copy web UI dist — from web-builder when ENABLE_EMBEDUI=true, empty dir otherwise.
COPY --from=web-dist /app/dist /src/internal/webui/dist
@@ -48,6 +49,7 @@ COPY --from=web-dist /app/dist /src/internal/webui/dist
RUN set -eux; \
if [ -z "$VERSION" ] && [ -f VERSION ]; then VERSION=$(cat VERSION); fi; \
if [ -z "$VERSION" ]; then VERSION="dev"; fi; \
if [ -z "$COMMIT_SHA" ]; then COMMIT_SHA="unknown"; fi; \
TAGS=""; \
if [ "$ENABLE_EMBEDUI" = "true" ]; then TAGS="embedui"; fi; \
if [ "$ENABLE_OTEL" = "true" ]; then \
@@ -61,7 +63,7 @@ RUN set -eux; \
fi; \
if [ -n "$TAGS" ]; then TAGS="-tags $TAGS"; fi; \
CGO_ENABLED=0 GOOS=linux \
go build -ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
go build -ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION} -X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=${COMMIT_SHA}" \
${TAGS} -o /out/goclaw . && \
CGO_ENABLED=0 GOOS=linux \
go build -ldflags="-s -w" -o /out/pkg-helper ./cmd/pkg-helper
+2 -1
View File
@@ -1,5 +1,6 @@
VERSION ?= $(shell git describe --tags --abbrev=0 --match "v[0-9]*" 2>/dev/null || echo dev)
LDFLAGS = -s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=$(VERSION)
COMMIT_SHA ?= $(shell git rev-parse HEAD 2>/dev/null || echo unknown)
LDFLAGS = -s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=$(VERSION) -X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=$(COMMIT_SHA)
BINARY = goclaw
.PHONY: build build-full build-tui run clean version up up-build down logs reset test vet check-web dev migrate setup ci desktop-dev desktop-build desktop-dmg test-hooks test-hooks-unit test-hooks-e2e test-hooks-chaos test-hooks-rbac test-hooks-tracing
+1 -1
View File
@@ -29,7 +29,7 @@ func doctorCmd() *cobra.Command {
func runDoctor() {
fmt.Println("goclaw doctor")
fmt.Printf(" Version: %s (protocol %d)\n", Version, protocol.ProtocolVersion)
fmt.Printf(" Version: %s%s (protocol %d)\n", Version, commitSuffix(), protocol.ProtocolVersion)
fmt.Printf(" OS: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Printf(" Go: %s\n", runtime.Version())
fmt.Println()
+16 -1
View File
@@ -12,6 +12,21 @@ import (
// Version is set at build time via -ldflags "-X github.com/nextlevelbuilder/goclaw/cmd.Version=v1.0.0"
var Version = "dev"
// CommitSHA is set at build time via -ldflags "-X github.com/nextlevelbuilder/goclaw/cmd.CommitSHA=<sha>".
// It records the exact source commit the binary was built from, so a running
// image/binary can be lined up with a commit even when VCS metadata is
// unavailable (e.g. Docker builds, where .git is excluded from the context).
var CommitSHA = "unknown"
// commitSuffix returns " (commit <sha>)" when CommitSHA was injected at build
// time, so version/doctor/upgrade output can carry release provenance.
func commitSuffix() string {
if CommitSHA == "" || CommitSHA == "unknown" {
return ""
}
return " (commit " + CommitSHA + ")"
}
var (
cfgFile string
verbose bool
@@ -64,7 +79,7 @@ func versionCmd() *cobra.Command {
Use: "version",
Short: "Print version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("goclaw %s (protocol %d)\n", Version, protocol.ProtocolVersion)
fmt.Printf("goclaw %s%s (protocol %d)\n", Version, commitSuffix(), protocol.ProtocolVersion)
},
}
}
+2 -2
View File
@@ -45,7 +45,7 @@ func runUpgradeStatus() error {
return fmt.Errorf("load config: %w", err)
}
fmt.Printf(" App version: %s (protocol %d)\n", Version, protocol.ProtocolVersion)
fmt.Printf(" App version: %s%s (protocol %d)\n", Version, commitSuffix(), protocol.ProtocolVersion)
if cfg.Database.PostgresDSN == "" {
fmt.Println(" Database: NOT CONFIGURED (set GOCLAW_POSTGRES_DSN)")
@@ -124,7 +124,7 @@ func runUpgrade(dryRun bool) error {
return fmt.Errorf("check schema: %w", err)
}
fmt.Printf(" App version: %s (protocol %d)\n", Version, protocol.ProtocolVersion)
fmt.Printf(" App version: %s%s (protocol %d)\n", Version, commitSuffix(), protocol.ProtocolVersion)
fmt.Printf(" Schema current: %d\n", s.CurrentVersion)
fmt.Printf(" Schema required: %d\n", s.RequiredVersion)
fmt.Println()
+1
View File
@@ -34,6 +34,7 @@ services:
ENABLE_PYTHON: "${ENABLE_PYTHON:-true}"
ENABLE_FULL_SKILLS: "${ENABLE_FULL_SKILLS:-false}"
VERSION: "${GOCLAW_VERSION:-dev}"
COMMIT_SHA: "${GOCLAW_COMMIT_SHA:-}"
# Uncomment to test update UI: GOCLAW_VERSION=v1.0.0
ports:
- "${GOCLAW_PORT:-18790}:18790"