From 52ced3710f391b63e482e9d8e1f868e5e8c7550b Mon Sep 17 00:00:00 2001 From: fchengyan <3181088318@qq.com> Date: Wed, 30 Sep 2026 16:38:53 +0800 Subject: [PATCH] fix(build): embed commit SHA for release provenance (#1571 part 2) (#1590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. --- .github/workflows/dev-beta-release.yaml | 5 +++-- .github/workflows/fork-image.yaml | 1 + .github/workflows/release-beta.yaml | 1 + .github/workflows/release.yaml | 3 ++- Dockerfile | 4 +++- Makefile | 3 ++- cmd/doctor.go | 2 +- cmd/root.go | 17 ++++++++++++++++- cmd/upgrade.go | 4 ++-- docker-compose.yml | 1 + 10 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dev-beta-release.yaml b/.github/workflows/dev-beta-release.yaml index b8b9665c..f723ca4a 100644 --- a/.github/workflows/dev-beta-release.yaml +++ b/.github/workflows/dev-beta-release.yaml @@ -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 }} diff --git a/.github/workflows/fork-image.yaml b/.github/workflows/fork-image.yaml index 0d8c8932..40b8d73b 100644 --- a/.github/workflows/fork-image.yaml +++ b/.github/workflows/fork-image.yaml @@ -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 diff --git a/.github/workflows/release-beta.yaml b/.github/workflows/release-beta.yaml index 2ace79ed..99da5998 100644 --- a/.github/workflows/release-beta.yaml +++ b/.github/workflows/release-beta.yaml @@ -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 }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index def05209..907edf7d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -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 diff --git a/Dockerfile b/Dockerfile index 9672f982..0bc49e2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Makefile b/Makefile index 635f44b4..38948c62 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/cmd/doctor.go b/cmd/doctor.go index aa916058..80f41303 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -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() diff --git a/cmd/root.go b/cmd/root.go index 4e812115..a659ad24 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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=". +// 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 )" 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) }, } } diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 2109a7d4..5fe9fd3d 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -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() diff --git a/docker-compose.yml b/docker-compose.yml index dcaddb0a..a289362d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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"