openclaw - ✅(Solved) Fix --version reports live git HEAD instead of the commit the built binary was built from [1 pull requests, 4 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#68148Fetched 2026-04-18 05:53:43
View on GitHub
Comments
4
Participants
2
Timeline
5
Reactions
0
Author
Timeline (top)
commented ×4cross-referenced ×1

node dist/index.js --version reports the current value of .git/HEAD, not the commit the dist/ was built from. The two disagree any time HEAD moves after a build, so --version is unreliable as a build-identity marker — even though dist/build-info.json still records the correct commit.

Root Cause

src/infra/git-commit.ts resolveCommitHash (lines 213-258) consults live .git/HEAD before falling back to dist/build-info.json. Whenever a live git checkout is present, the baked build-info is ignored even after HEAD has drifted past the built commit. The behavior is documented intent in the test suite — "prefers live git metadata over stale build info in a real checkout" (git-commit.test.ts:113).

That test made sense when the resolver only had two sources (live git vs. baked JSON file). But with a bundler-injected compile-time constant available, there's a third, strictly-better source: a short SHA literally compiled into the binary.

Fix Action

Fixed

PR fix notes

PR #68176: CLI: bake git commit at build time so --version reports built commit not live HEAD [AI-assisted]

Description (problem / solution / changelog)

Fixes #68148.

Summary

Before: node dist/index.js --version reports the current value of .git/HEAD, which can drift past the commit the binary was actually built from.

After: the short SHA is baked into dist/ at build time as a tsdown compile-time define, so --version reports what's executing — even if HEAD moves after the build.

Priority order

New: GIT_COMMIT/GIT_SHA env → injected define → live git → build-info.jsonpackage.json.gitHead.

Env override still wins (CI use case). Live git becomes the fallback for dev runs from source (no define injected), which is where the existing "prefers live git metadata over stale build info in a real checkout" test case (unchanged, still passing) captures correct behavior.

Changes

  • tsdown.config.ts: resolve short SHA at bundle time (env GIT_COMMIT/GIT_SHAgit rev-parse HEADundefined), pass as define.__OPENCLAW_GIT_COMMIT__ to every nodeBuildConfig output (covers both the unified core build and every buildBundledPluginConfigs entry).
  • src/infra/git-commit.ts: add declare const __OPENCLAW_GIT_COMMIT__, a readCommitFromInjectedDefine helper, and a readInjectedCommit slot on CommitMetadataReaders. resolveCommitHash consults it after the env override and cache check, before live git.
  • src/infra/git-commit.test.ts: 4 new cases covering injected-beats-live-git, env-still-overrides-injected, null-injected-falls-through, and cache-stability.

No change to scripts/write-build-info.tsdist/build-info.json remains for external tooling.

Repro (on upstream/main before the fix)

git worktree add -b repro /tmp/repro upstream/main
cd /tmp/repro
pnpm install --frozen-lockfile && pnpm build
git log -1 --format=%h                    # be7a415
node dist/index.js --version              # OpenClaw 2026.4.16 (be7a415)
git commit --allow-empty -m "shift HEAD"
node dist/index.js --version              # OpenClaw 2026.4.16 (d2d0ede)  ← lie

Same sequence after the fix: second --version still reports be7a415.

Testing

  • pnpm test src/infra/git-commit (via vitest.infra.config.ts) — 18/18 pass (14 existing + 4 new)
  • pnpm build — green
  • pnpm check — green
  • pnpm test — one flaky 120s-timeout in extensions/memory-core/src/memory/index.test.ts > "indexes multimodal image and audio files from extra paths with Gemini structured inputs"; zero import-path coupling to this PR's changes; passed cleanly on isolated re-run. All other 60 shards green.
  • End-to-end: built binary + empty commit + HEAD shift → --version stays stable at the built commit; env GIT_COMMIT=deadbee ... --version correctly reports deadbee (override preserved).

AI-assisted disclosure

  • Degree of testing: fully tested — focused unit lane + full pnpm build && pnpm check && pnpm test + end-to-end HEAD-shift repro both before and after the fix.
  • Tool: Claude Code (Opus 4.7). Sparkeros (PR author) reviewed and confirmed understanding of every change.
  • Prompt / session: the session explored the issue collaboratively — identified root cause in resolveCommitHash priority order, confirmed no existing upstream PR addresses it (PR #56372 is cache-only), chose the bundler-define approach over alternatives (file-based marker, live-git-with-disclaimer), deliberately kept scope narrow by not also wiring the vestigial __OPENCLAW_VERSION__ define.
  • I understand what this code does and why each line is there.

Changed files

  • src/infra/git-commit.test.ts (modified, +66/-0)
  • src/infra/git-commit.ts (modified, +14/-0)
  • tsdown.config.ts (modified, +23/-0)

Code Example

git worktree add -b repro/version-drift /tmp/openclaw-version-drift upstream/main
cd /tmp/openclaw-version-drift
pnpm install --frozen-lockfile
pnpm build

git log -1 --format=%h                        # be7a415  (commit-A)
cat dist/build-info.json                      # "commit": "be7a415eb09609408087d9d2737b6f02f36586ab"
node dist/index.js --version                  # OpenClaw 2026.4.16 (be7a415)
git commit --allow-empty -m "shift HEAD post-build"
git log -1 --format=%h                        # d2d0ede  (commit-B)
node dist/index.js --version                  # OpenClaw 2026.4.16 (d2d0ede)   ❌  built code still be7a415
cat dist/build-info.json                      # still be7a415                   ← file agrees, runtime does not
RAW_BUFFERClick to expand / collapse

Summary

node dist/index.js --version reports the current value of .git/HEAD, not the commit the dist/ was built from. The two disagree any time HEAD moves after a build, so --version is unreliable as a build-identity marker — even though dist/build-info.json still records the correct commit.

Reproduction (on upstream/main, commit be7a415eb0 at time of filing)

git worktree add -b repro/version-drift /tmp/openclaw-version-drift upstream/main
cd /tmp/openclaw-version-drift
pnpm install --frozen-lockfile
pnpm build

git log -1 --format=%h                        # be7a415  (commit-A)
cat dist/build-info.json                      # "commit": "be7a415eb09609408087d9d2737b6f02f36586ab"
node dist/index.js --version                  # OpenClaw 2026.4.16 (be7a415)   ✅

git commit --allow-empty -m "shift HEAD post-build"
git log -1 --format=%h                        # d2d0ede  (commit-B)
node dist/index.js --version                  # OpenClaw 2026.4.16 (d2d0ede)   ❌  built code still be7a415
cat dist/build-info.json                      # still be7a415                   ← file agrees, runtime does not

Nothing was rebuilt. The runtime lies about what code is executing.

Root cause

src/infra/git-commit.ts resolveCommitHash (lines 213-258) consults live .git/HEAD before falling back to dist/build-info.json. Whenever a live git checkout is present, the baked build-info is ignored even after HEAD has drifted past the built commit. The behavior is documented intent in the test suite — "prefers live git metadata over stale build info in a real checkout" (git-commit.test.ts:113).

That test made sense when the resolver only had two sources (live git vs. baked JSON file). But with a bundler-injected compile-time constant available, there's a third, strictly-better source: a short SHA literally compiled into the binary.

Real-world evidence

On a host running a built gateway:

SourceValue
dist/build-info.json commit923aa38 (actual built commit)
node dist/index.js --versionOpenClaw 2026.4.16 (80d9aa2) — current HEAD, not what's executing

Not addressed by existing PRs

  • PR #56372 touches the same file but only refactors caches; priority order unchanged.
  • PR #39712 introduced the commit suffix but the HEAD-drift case wasn't considered.

Proposed fix (happy to open the PR)

Bake the short SHA into dist/ at build time via a tsdown compile-time define (__OPENCLAW_GIT_COMMIT__), mirroring the existing __OPENCLAW_VERSION__ declaration pattern at src/version.ts:4. In resolveCommitHash, consult the injected constant after the GIT_COMMIT/GIT_SHA env override but before live git.

New priority: env > injected define > live git > build-info.json > package.json.gitHead.

Properties preserved:

  • GIT_COMMIT/GIT_SHA env overrides still win (CI use case).
  • Dev runs from source (tsx src/entry.ts) — no define injected, typeof guard falls through to live git.
  • dist/build-info.json stays as-is (external diagnostic artifact).
  • npm-installed packages (no live git checkout) continue to work via build-info fallback.

The existing test at git-commit.test.ts:113 continues to pass unchanged because the test environment has no injected define.

Environment

  • be7a415eb0 at time of report
  • pnpm 10.32.1 / Node 22 / WSL2 (Linux 6.6)

<sub>Filed with assistance from Claude Code.</sub>

extent analysis

TL;DR

The issue can be fixed by baking the short SHA into dist/ at build time via a tsdown compile-time define and updating the resolveCommitHash function to consult this injected constant.

Guidance

  • Update src/version.ts to include a __OPENCLAW_GIT_COMMIT__ declaration pattern, similar to the existing __OPENCLAW_VERSION__ declaration.
  • Modify the resolveCommitHash function in src/infra/git-commit.ts to consult the injected __OPENCLAW_GIT_COMMIT__ constant after checking for GIT_COMMIT/GIT_SHA env overrides, but before falling back to live git or dist/build-info.json.
  • Ensure the new priority order is: env > injected define > live git > build-info.json > package.json.gitHead.
  • Verify the fix by running the reproduction steps and checking that node dist/index.js --version reports the correct commit hash.

Example

// src/version.ts
declare const __OPENCLAW_VERSION__: string;
declare const __OPENCLAW_GIT_COMMIT__: string;

// src/infra/git-commit.ts
function resolveCommitHash() {
  // ...
  if (typeof __OPENCLAW_GIT_COMMIT__ !== 'undefined') {
    return __OPENCLAW_GIT_COMMIT__;
  }
  // ...
}

Notes

The proposed fix assumes that the tsdown compile-time define is properly configured to inject the __OPENCLAW_GIT_COMMIT__ constant at build time.

Recommendation

Apply the proposed fix by updating src/version.ts and src/infra/git-commit.ts as described, and then verify the fix by running the reproduction steps. This approach ensures that the correct commit hash is reported by node dist/index.js --version, even when the live git checkout has drifted past the built commit.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix --version reports live git HEAD instead of the commit the built binary was built from [1 pull requests, 4 comments, 2 participants]