| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { spawn, type ChildProcess } from "node:child_process"; | 1 | import { execFileSync, spawn, type ChildProcess } from "node:child_process"; |
| 2 | import { randomBytes } from "node:crypto"; | 2 | import { randomBytes } from "node:crypto"; |
| 3 | import fs from "node:fs"; | 3 | import fs from "node:fs"; |
| 4 | import net from "node:net"; | 4 | import net from "node:net"; |
| ... | @@ -26,6 +26,8 @@ const ROOT_DIR = path.resolve(__dirname, "../../.."); | ... | @@ -26,6 +26,8 @@ const ROOT_DIR = path.resolve(__dirname, "../../.."); |
| 26 | const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); | 26 | const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); |
| 27 | const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; | 27 | const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; |
| 28 | const APP_USER_MODEL_ID = "com.t3tools.t3code"; | 28 | const APP_USER_MODEL_ID = "com.t3tools.t3code"; |
| | 29 | const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i; |
| | 30 | const COMMIT_HASH_DISPLAY_LENGTH = 12; |
| 29 | | 31 | |
| 30 | let mainWindow: BrowserWindow | null = null; | 32 | let mainWindow: BrowserWindow | null = null; |
| 31 | let backendProcess: ChildProcess | null = null; | 33 | let backendProcess: ChildProcess | null = null; |
| ... | @@ -36,6 +38,7 @@ let restartAttempt = 0; | ... | @@ -36,6 +38,7 @@ let restartAttempt = 0; |
| 36 | let restartTimer: ReturnType<typeof setTimeout> | null = null; | 38 | let restartTimer: ReturnType<typeof setTimeout> | null = null; |
| 37 | let isQuitting = false; | 39 | let isQuitting = false; |
| 38 | let desktopProtocolRegistered = false; | 40 | let desktopProtocolRegistered = false; |
| | 41 | let aboutCommitHashCache: string | null | undefined; |
| 39 | | 42 | |
| 40 | let destructiveMenuIconCache: Electron.NativeImage | null | undefined; | 43 | let destructiveMenuIconCache: Electron.NativeImage | null | undefined; |
| 41 | | 44 | |
| ... | @@ -81,6 +84,58 @@ function resolveAppRoot(): string { | ... | @@ -81,6 +84,58 @@ function resolveAppRoot(): string { |
| 81 | return app.getAppPath(); | 84 | return app.getAppPath(); |
| 82 | } | 85 | } |
| 83 | | 86 | |
| | 87 | function normalizeCommitHash(value: unknown): string | null { |
| | 88 | if (typeof value !== "string") { |
| | 89 | return null; |
| | 90 | } |
| | 91 | const trimmed = value.trim(); |
| | 92 | if (!COMMIT_HASH_PATTERN.test(trimmed)) { |
| | 93 | return null; |
| | 94 | } |
| | 95 | return trimmed.slice(0, COMMIT_HASH_DISPLAY_LENGTH).toLowerCase(); |
| | 96 | } |
| | 97 | |
| | 98 | function resolveEmbeddedCommitHash(): string | null { |
| | 99 | const packageJsonPath = path.join(resolveAppRoot(), "package.json"); |
| | 100 | if (!fs.existsSync(packageJsonPath)) { |
| | 101 | return null; |
| | 102 | } |
| | 103 | |
| | 104 | try { |
| | 105 | const raw = fs.readFileSync(packageJsonPath, "utf8"); |
| | 106 | const parsed = JSON.parse(raw) as { t3codeCommitHash?: unknown }; |
| | 107 | return normalizeCommitHash(parsed.t3codeCommitHash); |
| | 108 | } catch { |
| | 109 | return null; |
| | 110 | } |
| | 111 | } |
| | 112 | |
| | 113 | function resolveGitCommitHash(): string | null { |
| | 114 | try { |
| | 115 | const stdout = execFileSync("git", ["rev-parse", `--short=${COMMIT_HASH_DISPLAY_LENGTH}`, "HEAD"], { |
| | 116 | cwd: ROOT_DIR, |
| | 117 | encoding: "utf8", |
| | 118 | stdio: ["ignore", "pipe", "ignore"], |
| | 119 | }); |
| | 120 | return normalizeCommitHash(stdout); |
| | 121 | } catch { |
| | 122 | return null; |
| | 123 | } |
| | 124 | } |
| | 125 | |
| | 126 | function resolveAboutCommitHash(): string | null { |
| | 127 | if (aboutCommitHashCache !== undefined) { |
| | 128 | return aboutCommitHashCache; |
| | 129 | } |
| | 130 | |
| | 131 | aboutCommitHashCache = |
| | 132 | normalizeCommitHash(process.env.T3CODE_COMMIT_HASH) ?? |
| | 133 | resolveEmbeddedCommitHash() ?? |
| | 134 | resolveGitCommitHash(); |
| | 135 | |
| | 136 | return aboutCommitHashCache; |
| | 137 | } |
| | 138 | |
| 84 | function resolveBackendEntry(): string { | 139 | function resolveBackendEntry(): string { |
| 85 | return path.join(resolveAppRoot(), "apps/server/dist/index.mjs"); | 140 | return path.join(resolveAppRoot(), "apps/server/dist/index.mjs"); |
| 86 | } | 141 | } |
| ... | @@ -277,8 +332,11 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null { | ... | @@ -277,8 +332,11 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null { |
| 277 | | 332 | |
| 278 | function configureAppIdentity(): void { | 333 | function configureAppIdentity(): void { |
| 279 | app.setName(APP_DISPLAY_NAME); | 334 | app.setName(APP_DISPLAY_NAME); |
| | 335 | const commitHash = resolveAboutCommitHash(); |
| 280 | app.setAboutPanelOptions({ | 336 | app.setAboutPanelOptions({ |
| 281 | applicationName: APP_DISPLAY_NAME, | 337 | applicationName: APP_DISPLAY_NAME, |
| | 338 | applicationVersion: app.getVersion(), |
| | 339 | version: commitHash ?? "unknown", |
| 282 | }); | 340 | }); |
| 283 | | 341 | |
| 284 | if (process.platform === "win32") { | 342 | if (process.platform === "win32") { |