From 6cbd3a6ac31683fb9558c199b74e44a81612fef8 Mon Sep 17 00:00:00 2001 From: ethernet Date: Tue, 28 Jul 2026 10:13:58 -0400 Subject: [PATCH] fix(version): preserve unavailable Nix branch metadata Stamp absent branches as JSON null instead of the ambiguous "unknown" sentinel. Show an explicit no-branch-information value in Desktop version details. --- apps/desktop/electron/main.ts | 2 +- .../src/components/version-details.test.tsx | 41 +++++++++++++++++++ .../src/components/version-details.tsx | 10 ++--- apps/desktop/src/global.d.ts | 2 +- apps/desktop/src/i18n/en.ts | 1 + apps/desktop/src/i18n/ja.ts | 1 + apps/desktop/src/i18n/types.ts | 1 + apps/desktop/src/i18n/zh-hant.ts | 1 + apps/desktop/src/i18n/zh.ts | 1 + nix/desktop.nix | 6 +-- nix/hermes-agent.nix | 3 +- nix/packages.nix | 6 ++- scripts/write_install_stamp.py | 2 - tests/hermes_cli/test_version_info.py | 6 +-- 14 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 apps/desktop/src/components/version-details.test.tsx diff --git a/apps/desktop/electron/main.ts b/apps/desktop/electron/main.ts index 4a2c1ce6c8c..ce8aa7d8382 100644 --- a/apps/desktop/electron/main.ts +++ b/apps/desktop/electron/main.ts @@ -11069,7 +11069,7 @@ function resolveHermesVersionInfo() { baseVersion: INSTALL_STAMP.baseVersion ?? undefined, distance: INSTALL_STAMP.distance ?? undefined, commit: INSTALL_STAMP.commit, - branch: INSTALL_STAMP.branch ?? undefined, + branch: INSTALL_STAMP.branch, source: INSTALL_STAMP.source ?? undefined, installMethod: INSTALL_STAMP.installMethod ?? undefined, dirty: INSTALL_STAMP.dirty diff --git a/apps/desktop/src/components/version-details.test.tsx b/apps/desktop/src/components/version-details.test.tsx new file mode 100644 index 00000000000..a720d80d565 --- /dev/null +++ b/apps/desktop/src/components/version-details.test.tsx @@ -0,0 +1,41 @@ +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, describe, expect, it } from 'vitest' + +import type { DesktopVersionInfo } from '@/global' +import { I18nProvider } from '@/i18n' + +import { VersionDetails } from './version-details' + +const baseVersion: DesktopVersionInfo = { + appVersion: '0.19.0', + electronVersion: '37.0.0', + hermesRoot: '/tmp/hermes', + nodeVersion: '22.0.0', + platform: 'linux' +} + +afterEach(cleanup) + +describe('VersionDetails', () => { + it('labels a missing branch explicitly', () => { + render( + + + + ) + + expect(screen.getByText('Branch')).toBeTruthy() + expect(screen.getByText('No branch information')).toBeTruthy() + }) + + it('preserves a literal branch named unknown', () => { + render( + + + + ) + + expect(screen.getByText('unknown')).toBeTruthy() + expect(screen.queryByText('No branch information')).toBeNull() + }) +}) diff --git a/apps/desktop/src/components/version-details.tsx b/apps/desktop/src/components/version-details.tsx index 2e6f24e76bc..498ace7ede2 100644 --- a/apps/desktop/src/components/version-details.tsx +++ b/apps/desktop/src/components/version-details.tsx @@ -25,12 +25,10 @@ export function VersionDetails({ version }: { version: DesktopVersionInfo }) {
{version.baseVersion}
)} - {version.branch && ( -
-
{u.versionDetailsBranch}
-
{version.branch}
-
- )} +
+
{u.versionDetailsBranch}
+
{version.branch ?? u.versionDetailsNoBranchInfo}
+
{version.commit && (
{u.versionDetailsCommit}
diff --git a/apps/desktop/src/global.d.ts b/apps/desktop/src/global.d.ts index 3b4f4f1c305..241d558e23d 100644 --- a/apps/desktop/src/global.d.ts +++ b/apps/desktop/src/global.d.ts @@ -320,7 +320,7 @@ export interface HermesTerminalExit { export interface DesktopVersionInfo { appVersion: string baseVersion?: string - branch?: string + branch?: string | null commit?: string distance?: number dirty?: boolean diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index f33335a990e..6746449611b 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -2132,6 +2132,7 @@ export const en: Translations = { versionDetailsVersion: 'Version', versionDetailsBaseVersion: 'Release version', versionDetailsBranch: 'Branch', + versionDetailsNoBranchInfo: 'No branch information', versionDetailsCommit: 'Commit', versionDetailsDirty: 'This package was built from a dirty source tree.', versionDetailsDirtyUnknown: 'Built from a dirty source tree. The number of changes since the last release is unknown.', diff --git a/apps/desktop/src/i18n/ja.ts b/apps/desktop/src/i18n/ja.ts index 2d16d792ec5..6bbc48ba4cb 100644 --- a/apps/desktop/src/i18n/ja.ts +++ b/apps/desktop/src/i18n/ja.ts @@ -1992,6 +1992,7 @@ export const ja = defineLocale({ versionDetailsVersion: 'バージョン', versionDetailsBaseVersion: 'リリースバージョン', versionDetailsBranch: 'ブランチ', + versionDetailsNoBranchInfo: 'ブランチ情報なし', versionDetailsCommit: 'コミット', versionDetailsDirty: 'このパッケージは変更のあるソースツリーからビルドされました。', versionDetailsDirtyUnknown: '変更のあるソースツリーからビルドされました。最後のリリースからの変更数は不明です。', diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index 8039339c24d..6d7211ec1eb 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -1771,6 +1771,7 @@ export interface Translations { versionDetailsVersion: string versionDetailsBaseVersion: string versionDetailsBranch: string + versionDetailsNoBranchInfo: string versionDetailsCommit: string versionDetailsDirty: string versionDetailsDirtyUnknown: string diff --git a/apps/desktop/src/i18n/zh-hant.ts b/apps/desktop/src/i18n/zh-hant.ts index d55d46c2559..5b372a113c5 100644 --- a/apps/desktop/src/i18n/zh-hant.ts +++ b/apps/desktop/src/i18n/zh-hant.ts @@ -1932,6 +1932,7 @@ export const zhHant = defineLocale({ versionDetailsVersion: '版本', versionDetailsBaseVersion: '發行版本', versionDetailsBranch: '分支', + versionDetailsNoBranchInfo: '沒有分支資訊', versionDetailsCommit: '提交', versionDetailsDirty: '此套件從有未提交變更的來源樹建置。', versionDetailsDirtyUnknown: '從有未提交變更的來源樹建置。自上次發布以來的變更數量未知。', diff --git a/apps/desktop/src/i18n/zh.ts b/apps/desktop/src/i18n/zh.ts index fd116f3f247..d8b05bfe5b5 100644 --- a/apps/desktop/src/i18n/zh.ts +++ b/apps/desktop/src/i18n/zh.ts @@ -2323,6 +2323,7 @@ export const zh: Translations = { versionDetailsVersion: '版本', versionDetailsBaseVersion: '发布版本', versionDetailsBranch: '分支', + versionDetailsNoBranchInfo: '无分支信息', versionDetailsCommit: '提交', versionDetailsDirty: '此软件包从有未提交更改的源代码树构建。', versionDetailsDirtyUnknown: '从有未提交更改的源代码树构建。自上次发布以来的更改数量未知。', diff --git a/nix/desktop.nix b/nix/desktop.nix index 430e3860ba1..b71a7a5aab0 100644 --- a/nix/desktop.nix +++ b/nix/desktop.nix @@ -44,10 +44,6 @@ let if distance != null && distance > 0 then "${version}+${toString distance}" else if dirty && distance == null then "${version}+?" else version; - # On a dirty tree, sourceInfo.ref is null (flakes can't determine the ref). - # Show "unknown" rather than null so the desktop doesn't fall back to its - # self-update default branch ("main") when the real branch is unavailable. - branchLabel = if branch != null then branch else "unknown"; electronHeaders = pkgs.fetchurl { url = "https://artifacts.electronjs.org/headers/dist/v${electron.version}/node-v${electron.version}-headers.tar.gz"; @@ -166,7 +162,7 @@ let cp -rn apps/desktop/dist $out/ cat > $out/install-stamp.json <<'EOF' - {"schemaVersion":2,"commit":${builtins.toJSON rev},"branch":${builtins.toJSON branchLabel},"baseVersion":"${version}","displayVersion":"${displayVersion}","distance":${builtins.toJSON distance},"dirty":${ + {"schemaVersion":2,"commit":${builtins.toJSON rev},"branch":${builtins.toJSON branch},"baseVersion":"${version}","displayVersion":"${displayVersion}","distance":${builtins.toJSON distance},"dirty":${ if dirty then "true" else "false" },"source":"nix","installMethod":"nix"} EOF diff --git a/nix/hermes-agent.nix b/nix/hermes-agent.nix index 228df8f24c5..c84fd4604aa 100644 --- a/nix/hermes-agent.nix +++ b/nix/hermes-agent.nix @@ -56,7 +56,6 @@ let if stampDistance != null && stampDistance > 0 then "${version}+${toString stampDistance}" else if dirty && stampDistance == null then "${version}+?" else version; - stampBranch = if branch != null then branch else "unknown"; nodejs = nodejs_22; mkHermesVenv = @@ -206,7 +205,7 @@ stdenv.mkDerivation (finalAttrs: { # runtime instead of probing env vars or .git — one file, one source # of truth for the Python runtime (CLI, TUI). cat > $out/share/hermes-agent/.hermes_build_info.json < git if dirty is None: diff --git a/tests/hermes_cli/test_version_info.py b/tests/hermes_cli/test_version_info.py index 2b1cada5c57..4a23f771a05 100644 --- a/tests/hermes_cli/test_version_info.py +++ b/tests/hermes_cli/test_version_info.py @@ -47,11 +47,11 @@ def test_stamp_version_info_reads_nix_stamp(tmp_path, monkeypatch): assert info == VersionInfo("0.19.0", "0.19.0+3", 3, "a" * 40, "feature/version", "nix") -def test_stamp_version_info_reads_docker_stamp_with_unknown_distance(tmp_path, monkeypatch): +def test_stamp_version_info_preserves_missing_branch(tmp_path, monkeypatch): stamp = { "schemaVersion": 2, "commit": "b" * 40, - "branch": "unknown", + "branch": None, "baseVersion": "0.19.0", "displayVersion": "0.19.0+?", "distance": None, @@ -64,7 +64,7 @@ def test_stamp_version_info_reads_docker_stamp_with_unknown_distance(tmp_path, m info = get_version_info() - assert info == VersionInfo("0.19.0", "0.19.0+?", None, "b" * 40, "unknown", "docker", True) + assert info == VersionInfo("0.19.0", "0.19.0+?", None, "b" * 40, None, "docker", True) def test_stamp_version_info_ignores_fallback_commit(tmp_path, monkeypatch):