/** * Generates src/generated/versionInfo.json with the project's current version, * git tag/describe info, and the last commit's metadata. Consumed by the admin * VersionOverlay via GET /api/version (the file is read at runtime so a fresh * post-bump regeneration rsync'd to the server shows up without a rebuild). * * Git metadata resolution order (first non-empty wins): * 1. Environment variables — explicit overrides for CI systems that inject * metadata as env vars: * GIT_TAG, GIT_DESCRIBE, GIT_COMMIT_HASH, GIT_COMMIT_DATE, GIT_COMMIT_SUBJECT * 2. SOURCE_COMMIT — the commit SHA Coolify injects as a build arg when * "Include Source Commit in Build" is enabled (application settings → * Advanced). Coolify strips `.git` from the build context, so `git` is * unavailable there. Only the SHA is provided — tags, date, and subject * have no Coolify equivalent and stay null in that case. * 3. `git` from the project root — the local-dev path (and any build context * that still contains .git, e.g. a local `docker build`). * * Gracefully no-ops on missing git metadata (no .git, no tags, no env) so the * JSON always exists with whatever fields are resolvable. */ import { execSync } from "node:child_process"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const here = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.resolve(here, "..", ".."); function git(args: string): string | null { try { const out = execSync(`git ${args}`, { cwd: projectRoot, encoding: "utf8" }); const trimmed = out.trim(); return trimmed.length > 0 ? trimmed : null; } catch { return null; } } function envVar(name: string): string | null { const value = process.env[name]; return typeof value === "string" && value.length > 0 ? value : null; } function shortCommit(sha: string): string { return sha.length > 7 ? sha.slice(0, 7) : sha; } function readPackageVersion(): string | null { try { const pkg = JSON.parse(readFileSync(path.join(projectRoot, "package.json"), "utf8")); return typeof pkg?.version === "string" && pkg.version.length > 0 ? pkg.version : null; } catch { return null; } } function main(): void { const version = readPackageVersion(); const tag = envVar("GIT_TAG") ?? git("describe --tags --always --abbrev=0"); const gitDescribe = envVar("GIT_DESCRIBE") ?? git("describe --tags --always --abbrev=7"); const rawCommit = envVar("GIT_COMMIT_HASH") ?? envVar("SOURCE_COMMIT"); const commitHash = rawCommit ? shortCommit(rawCommit) : git("rev-parse --short HEAD"); const commitDate = envVar("GIT_COMMIT_DATE") ?? git("log -1 --format=%cI"); const commitSubject = envVar("GIT_COMMIT_SUBJECT") ?? git("log -1 --format=%s"); const buildTime = new Date().toISOString(); const info = { version, tag, gitDescribe, commitHash, commitDate, commitSubject, lastUpdated: commitDate ?? buildTime, commitMessage: commitSubject, buildTime, }; const outDir = path.join(projectRoot, "src", "generated"); if (!existsSync(outDir)) { mkdirSync(outDir, { recursive: true }); } writeFileSync( path.join(outDir, "versionInfo.json"), `${JSON.stringify(info, null, 2)}\n`, "utf8", ); const label = gitDescribe ?? tag ?? version ?? "unknown"; console.log(`[version-info] wrote src/generated/versionInfo.json (${label})`); } main();