Skip to content

Commit 5ec2734

Browse files
authored
ENG-1926 Add build commit metadata to Roam builds (#1143)
* Add build commit metadata to Roam builds * Scope build env vars to the build task * Simplify roam build metadata injection
1 parent c8e7280 commit 5ec2734

11 files changed

Lines changed: 187 additions & 13 deletions

File tree

.github/workflows/roam-pr.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ on:
1212
- "packages/ui/**"
1313
env:
1414
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
15+
DISCOURSE_GRAPH_BUILD_COMMIT: ${{ github.event.pull_request.head.sha }}
16+
DISCOURSE_GRAPH_BUILD_BRANCH: ${{ github.head_ref }}
1517
GITHUB_HEAD_REF: ${{ github.head_ref }}
1618
GITHUB_REF_NAME: ${{ github.ref_name }}
1719
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}

apps/roam/scripts/compile.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import esbuild from "esbuild";
2+
import { execSync } from "child_process";
23
import fs from "fs";
34
import path from "path";
45
import { z } from "zod";
@@ -18,6 +19,38 @@ const getVersion = (): string => {
1819
const getBuildDate = (): string => {
1920
return new Date().toISOString().split("T")[0]; // YYYY-MM-DD format
2021
};
22+
23+
const runGitCommand = (command: string): string | undefined => {
24+
try {
25+
return execSync(command, {
26+
cwd: process.cwd(),
27+
stdio: ["ignore", "pipe", "ignore"],
28+
})
29+
.toString()
30+
.trim();
31+
} catch {
32+
return undefined;
33+
}
34+
};
35+
36+
const getBuildCommit = (): string => {
37+
return (
38+
process.env.DISCOURSE_GRAPH_BUILD_COMMIT ||
39+
process.env.GITHUB_SHA ||
40+
runGitCommand("git rev-parse HEAD") ||
41+
"-"
42+
);
43+
};
44+
45+
const getBuildBranch = (): string => {
46+
return (
47+
process.env.DISCOURSE_GRAPH_BUILD_BRANCH ||
48+
process.env.GITHUB_HEAD_REF ||
49+
process.env.GITHUB_REF_NAME ||
50+
runGitCommand("git rev-parse --abbrev-ref HEAD") ||
51+
"-"
52+
);
53+
};
2154
let envContents = null;
2255

2356
try {
@@ -168,6 +201,8 @@ export const compile = ({
168201
"process.env.NEXT_API_ROOT": `"${dbEnv.NEXT_API_ROOT || ""}"`,
169202
"window.__DISCOURSE_GRAPH_VERSION__": `"${getVersion()}"`,
170203
"window.__DISCOURSE_GRAPH_BUILD_DATE__": `"${getBuildDate()}"`,
204+
"window.__DISCOURSE_GRAPH_BUILD_COMMIT__": `"${getBuildCommit()}"`,
205+
"window.__DISCOURSE_GRAPH_BUILD_BRANCH__": `"${getBuildBranch()}"`,
171206
},
172207
sourcemap: process.env.NODE_ENV === "production" ? "external" : "inline",
173208
minify: process.env.NODE_ENV === "production",

apps/roam/src/components/settings/Settings.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const SettingsDialog = ({
110110
const [showAdminPanel, setShowAdminPanel] = useState(
111111
window.roamAlphaAPI.graph.name === "discourse-graphs" || false,
112112
);
113-
const { version, buildDate } = getVersionWithDate();
113+
const { versionStamp } = getVersionWithDate();
114114
const openAdminPanel = (): void => {
115115
setShowAdminPanel(true);
116116
setActiveTabId(ADMIN_TAB_ID);
@@ -349,9 +349,7 @@ export const SettingsDialog = ({
349349
Admin
350350
</Button>
351351
)}
352-
<span className="text-xs text-gray-500">
353-
v{version}-{buildDate}
354-
</span>
352+
<span className="text-xs text-gray-500">v{versionStamp}</span>
355353
</div>
356354
{/* <Button
357355
icon="cross"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { describe, expect, it } from "vitest";
2+
import { createVersionStamp, normalizeBuildBranch } from "~/utils/getVersion";
3+
4+
describe("createVersionStamp", () => {
5+
it("includes the short commit SHA for main builds", () => {
6+
expect(
7+
createVersionStamp({
8+
version: "0.20.0",
9+
buildDate: "2026-06-22",
10+
buildBranch: "main",
11+
buildCommit: "1234567890abcdef",
12+
}),
13+
).toBe("0.20.0-2026-06-22-12345678");
14+
});
15+
16+
it("includes a normalized branch between the date and commit SHA", () => {
17+
expect(
18+
createVersionStamp({
19+
version: "0.20.0",
20+
buildDate: "2026-06-22",
21+
buildBranch: "feature/branch build",
22+
buildCommit: "abcdef1234567890",
23+
}),
24+
).toBe("0.20.0-2026-06-22-feature-branch-build-abcdef12");
25+
});
26+
27+
it("falls back to the existing date stamp when Git metadata is missing", () => {
28+
expect(
29+
createVersionStamp({
30+
version: "0.20.0",
31+
buildDate: "2026-06-22",
32+
buildBranch: " ",
33+
buildCommit: "-",
34+
}),
35+
).toBe("0.20.0-2026-06-22");
36+
});
37+
});
38+
39+
describe("normalizeBuildBranch", () => {
40+
it("omits branch metadata for main and detached HEAD builds", () => {
41+
expect(normalizeBuildBranch("main")).toBeUndefined();
42+
expect(normalizeBuildBranch("HEAD")).toBeUndefined();
43+
});
44+
});

apps/roam/src/utils/getVersion.ts

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,89 @@ declare global {
33
interface Window {
44
__DISCOURSE_GRAPH_VERSION__?: string;
55
__DISCOURSE_GRAPH_BUILD_DATE__?: string;
6+
__DISCOURSE_GRAPH_BUILD_COMMIT__?: string;
7+
__DISCOURSE_GRAPH_BUILD_BRANCH__?: string;
68
}
79
}
810

9-
export const getVersionWithDate = (): {
11+
export type VersionMetadata = {
1012
version: string;
1113
buildDate: string;
12-
} => {
14+
buildCommit: string;
15+
buildBranch: string;
16+
versionStamp: string;
17+
};
18+
19+
const SHORT_COMMIT_LENGTH = 8;
20+
const FALLBACK_VALUE = "-";
21+
22+
const hasMetadataValue = (value: string | undefined): value is string =>
23+
Boolean(value?.trim() && value.trim() !== FALLBACK_VALUE);
24+
25+
export const normalizeBuildBranch = (
26+
buildBranch: string | undefined,
27+
): string | undefined => {
28+
if (!hasMetadataValue(buildBranch)) return undefined;
29+
30+
const branch = buildBranch.trim();
31+
if (
32+
!branch ||
33+
branch === "main" ||
34+
branch === "master" ||
35+
branch === "HEAD"
36+
) {
37+
return undefined;
38+
}
39+
40+
const normalized = branch
41+
.replace(/[^a-zA-Z0-9._-]+/g, "-")
42+
.replace(/^-+|-+$/g, "");
43+
return normalized || undefined;
44+
};
45+
46+
const getShortCommit = (
47+
buildCommit: string | undefined,
48+
): string | undefined => {
49+
if (!hasMetadataValue(buildCommit)) return undefined;
50+
return buildCommit.trim().slice(0, SHORT_COMMIT_LENGTH);
51+
};
52+
53+
export const createVersionStamp = ({
54+
version,
55+
buildDate,
56+
buildCommit,
57+
buildBranch,
58+
}: Omit<VersionMetadata, "versionStamp">): string => {
59+
const parts = [
60+
version || FALLBACK_VALUE,
61+
buildDate || FALLBACK_VALUE,
62+
normalizeBuildBranch(buildBranch),
63+
getShortCommit(buildCommit),
64+
].filter(hasMetadataValue);
65+
66+
return parts.length > 0 ? parts.join("-") : FALLBACK_VALUE;
67+
};
68+
69+
export const getVersionWithDate = (): VersionMetadata => {
1370
if (typeof window === "undefined") {
1471
return {
15-
version: "-",
16-
buildDate: "-",
72+
version: FALLBACK_VALUE,
73+
buildDate: FALLBACK_VALUE,
74+
buildCommit: FALLBACK_VALUE,
75+
buildBranch: FALLBACK_VALUE,
76+
versionStamp: FALLBACK_VALUE,
1777
};
1878
}
1979

80+
const metadata = {
81+
version: window.__DISCOURSE_GRAPH_VERSION__ || FALLBACK_VALUE,
82+
buildDate: window.__DISCOURSE_GRAPH_BUILD_DATE__ || FALLBACK_VALUE,
83+
buildCommit: window.__DISCOURSE_GRAPH_BUILD_COMMIT__ || FALLBACK_VALUE,
84+
buildBranch: window.__DISCOURSE_GRAPH_BUILD_BRANCH__ || FALLBACK_VALUE,
85+
};
86+
2087
return {
21-
version: window.__DISCOURSE_GRAPH_VERSION__ || "-",
22-
buildDate: window.__DISCOURSE_GRAPH_BUILD_DATE__ || "-",
88+
...metadata,
89+
versionStamp: createVersionStamp(metadata),
2390
};
2491
};

apps/roam/src/utils/posthog.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,17 @@ export const initPostHog = (): void => {
3838
return result;
3939
},
4040
loaded: (posthog) => {
41-
const { version, buildDate } = getVersionWithDate();
41+
const { version, buildDate, buildCommit, buildBranch, versionStamp } =
42+
getVersionWithDate();
4243
const userUid = window.roamAlphaAPI.user.uid() || "";
4344
const graphName = window.roamAlphaAPI.graph.name;
4445
if (userUid) posthog.identify(userUid);
4546
posthog.register_for_session({
4647
version: version || "-",
4748
buildDate: buildDate || "-",
49+
buildCommit: buildCommit || "-",
50+
buildBranch: buildBranch || "-",
51+
versionStamp: versionStamp || "-",
4852
graphName,
4953
});
5054
posthog.capture("Extension Loaded");

apps/roam/src/utils/sendErrorEmail.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const sendErrorEmail = async ({
1616
getNodeEnv() === "development"
1717
? "http://localhost:3000/api/errors"
1818
: "https://discoursegraphs.com/api/errors";
19-
const { version, buildDate } = getVersionWithDate();
19+
const { version, buildDate, buildCommit, buildBranch, versionStamp } =
20+
getVersionWithDate();
2021
const username = getCurrentUserDisplayName();
2122

2223
const payload: ErrorEmailProps = {
@@ -27,6 +28,9 @@ const sendErrorEmail = async ({
2728
graphName: window.roamAlphaAPI?.graph?.name || "unknown",
2829
version: version || "-",
2930
buildDate: buildDate || "-",
31+
buildCommit: buildCommit || "-",
32+
buildBranch: buildBranch || "-",
33+
versionStamp: versionStamp || "-",
3034
username: username || "unknown",
3135
context,
3236
};

apps/website/app/api/errors/EmailTemplate.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export const EmailTemplate = ({
1616
graphName,
1717
version,
1818
buildDate,
19+
buildCommit,
20+
buildBranch,
21+
versionStamp,
1922
username,
2023
context,
2124
}: ErrorEmailProps) => {
@@ -28,6 +31,9 @@ export const EmailTemplate = ({
2831
<ErrorField label="Username" value={username} />
2932
<ErrorField label="Version" value={version} />
3033
<ErrorField label="Build Date" value={buildDate} />
34+
<ErrorField label="Build Commit" value={buildCommit ?? "-"} />
35+
<ErrorField label="Build Branch" value={buildBranch ?? "-"} />
36+
<ErrorField label="Version Stamp" value={versionStamp ?? "-"} />
3137

3238
<div>
3339
<h2>Error Details</h2>

apps/website/app/api/errors/route.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export const POST = async (request: Request) => {
3838
username,
3939
version,
4040
buildDate,
41+
buildCommit,
42+
buildBranch,
43+
versionStamp,
4144
context = {},
4245
} = body;
4346

@@ -65,6 +68,9 @@ export const POST = async (request: Request) => {
6568
username,
6669
version,
6770
buildDate,
71+
buildCommit,
72+
buildBranch,
73+
versionStamp,
6874
context,
6975
}),
7076
});

packages/types/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export type ErrorEmailProps = {
66
graphName: string;
77
version: string;
88
buildDate: string;
9+
buildCommit?: string;
10+
buildBranch?: string;
11+
versionStamp?: string;
912
username: string;
1013
context?: Record<string, unknown>;
1114
};

0 commit comments

Comments
 (0)