CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
build-info.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 05cdb85 | 1 | /** |
| 2 | * Build info — captured once at boot, exposed everywhere. | |
| 3 | * | |
| 4 | * Reads git HEAD + boot time on first access so the running process can | |
| 5 | * tell anyone (the /api/version endpoint, the footer SHA stamp, the | |
| 6 | * client-side update poller) exactly which commit it's serving. | |
| 7 | * | |
| 8 | * Falls back gracefully when the .git directory isn't available | |
| 9 | * (e.g. running from a Docker image that didn't include it). | |
| 10 | */ | |
| 11 | ||
| 12 | import { spawnSync } from "child_process"; | |
| 13 | import { existsSync } from "fs"; | |
| 14 | import { join } from "path"; | |
| 15 | ||
| 16 | export interface BuildInfo { | |
| 17 | sha: string; // 7-char short sha | |
| 18 | shaFull: string; // 40-char full sha | |
| 19 | branch: string; | |
| 20 | builtAt: string; // ISO of process boot | |
| 21 | uptimeMs: number; // computed at access time | |
| 22 | } | |
| 23 | ||
| 24 | const STARTED_AT = Date.now(); | |
| 25 | ||
| 26 | let _cached: Omit<BuildInfo, "uptimeMs"> | null = null; | |
| 27 | ||
| 28 | function read(): Omit<BuildInfo, "uptimeMs"> { | |
| 29 | if (_cached) return _cached; | |
| 30 | ||
| 31 | // Honour explicit env overrides first (set by the deploy script in | |
| 32 | // immutable container images that strip the .git directory). | |
| 33 | const envSha = process.env.GIT_SHA?.trim(); | |
| 34 | const envBranch = process.env.GIT_BRANCH?.trim(); | |
| 35 | if (envSha) { | |
| 36 | _cached = { | |
| 37 | sha: envSha.slice(0, 7), | |
| 38 | shaFull: envSha, | |
| 39 | branch: envBranch || "main", | |
| 40 | builtAt: new Date(STARTED_AT).toISOString(), | |
| 41 | }; | |
| 42 | return _cached; | |
| 43 | } | |
| 44 | ||
| 45 | // Otherwise read from local .git | |
| 46 | const cwd = process.cwd(); | |
| 47 | const hasGit = existsSync(join(cwd, ".git")); | |
| 48 | if (!hasGit) { | |
| 49 | _cached = { | |
| 50 | sha: "unknown", | |
| 51 | shaFull: "unknown", | |
| 52 | branch: "unknown", | |
| 53 | builtAt: new Date(STARTED_AT).toISOString(), | |
| 54 | }; | |
| 55 | return _cached; | |
| 56 | } | |
| 57 | ||
| 58 | try { | |
| 59 | const sha = spawnSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }); | |
| 60 | const branch = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd, encoding: "utf8" }); | |
| 61 | const shaFull = (sha.stdout || "").trim(); | |
| 62 | _cached = { | |
| 63 | sha: shaFull.slice(0, 7) || "unknown", | |
| 64 | shaFull: shaFull || "unknown", | |
| 65 | branch: (branch.stdout || "").trim() || "main", | |
| 66 | builtAt: new Date(STARTED_AT).toISOString(), | |
| 67 | }; | |
| 68 | } catch { | |
| 69 | _cached = { | |
| 70 | sha: "unknown", | |
| 71 | shaFull: "unknown", | |
| 72 | branch: "unknown", | |
| 73 | builtAt: new Date(STARTED_AT).toISOString(), | |
| 74 | }; | |
| 75 | } | |
| 76 | return _cached; | |
| 77 | } | |
| 78 | ||
| 79 | export function getBuildInfo(): BuildInfo { | |
| 80 | const base = read(); | |
| 81 | return { | |
| 82 | ...base, | |
| 83 | uptimeMs: Date.now() - STARTED_AT, | |
| 84 | }; | |
| 85 | } |