import { execFileSync, spawnSync } from "node:child_process"; import { mkdirSync, readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { build } from "esbuild"; const projectRoot = fileURLToPath(new URL("../", import.meta.url)); const packageJson = JSON.parse( readFileSync(resolve(projectRoot, "package.json"), "utf8"), ); function getRevision() { const suppliedRevision = process.env.DOCKDECK_BUILD_REVISION?.trim(); if (suppliedRevision) return normalizeRevision(suppliedRevision); try { return normalizeRevision( execFileSync("git", ["rev-parse", "--verify", "HEAD"], { cwd: projectRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }).trim(), ); } catch { return "development"; } } function normalizeRevision(revision) { if (revision === "development") return revision; const normalizedRevision = revision.toLowerCase(); if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(normalizedRevision)) { throw new Error( "DOCKDECK_BUILD_REVISION must be a complete 40- or 64-character Git revision.", ); } return normalizedRevision; } function getBuildDate() { const buildDate = process.env.DOCKDECK_BUILD_DATE ?? new Date().toISOString(); const parsedDate = new Date(buildDate); const hasMilliseconds = /\.\d{3}Z$/.test(buildDate); const canonicalBuildDate = hasMilliseconds ? buildDate : `${buildDate.slice(0, -1)}.000Z`; if ( !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(buildDate) || Number.isNaN(parsedDate.getTime()) || parsedDate.toISOString() !== canonicalBuildDate ) { throw new Error( "DOCKDECK_BUILD_DATE must be a canonical UTC ISO timestamp with optional milliseconds.", ); } return buildDate; } const suppliedVersion = process.env.DOCKDECK_BUILD_VERSION?.trim(); if (suppliedVersion && suppliedVersion !== packageJson.version) { throw new Error( `DOCKDECK_BUILD_VERSION (${suppliedVersion}) does not match package.json (${packageJson.version}).`, ); } const revision = getRevision(); const buildDate = getBuildDate(); if (revision !== "development" && Date.parse(buildDate) === 0) { throw new Error( "DOCKDECK_BUILD_DATE must be a real UTC build timestamp when a full Git revision is provided.", ); } const buildInfo = { version: packageJson.version, revision, shortRevision: revision === "development" ? "dev" : revision.slice(0, 12), buildDate, }; function runNodeScript(scriptPath, args = []) { const result = spawnSync( process.execPath, [resolve(projectRoot, scriptPath), ...args], { cwd: projectRoot, stdio: "inherit" }, ); if (result.error) throw result.error; if (result.status !== 0) process.exit(result.status ?? 1); } runNodeScript("node_modules/typescript/bin/tsc", ["-b"]); runNodeScript("node_modules/vite/bin/vite.js", ["build"]); const serverOutput = resolve(projectRoot, "dist/server/index.js"); mkdirSync(dirname(serverOutput), { recursive: true }); await build({ entryPoints: [resolve(projectRoot, "src/server/index.ts")], bundle: true, platform: "node", format: "esm", packages: "external", outfile: serverOutput, define: { __DOCKDECK_BUILD_INFO__: JSON.stringify(buildInfo), }, });