mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-14 11:16:06 +00:00
* feat: show build identity in About screens * chore: leave root changelog to release automation * fix: translate Control UI About build details
157 lines
5.0 KiB
TypeScript
157 lines
5.0 KiB
TypeScript
// Build info tests cover canonical package provenance generation.
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
normalizeBuildCommit,
|
|
normalizeBuildTimestamp,
|
|
resolveBuildInfo,
|
|
writeBuildInfo,
|
|
} from "../../scripts/write-build-info.ts";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
|
|
describe("write-build-info", () => {
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
function createPackage(version = "2026.7.10"): string {
|
|
const rootDir = tempDirs.make("openclaw-build-info-");
|
|
fs.writeFileSync(path.join(rootDir, "package.json"), `${JSON.stringify({ version })}\n`);
|
|
return rootDir;
|
|
}
|
|
|
|
it("normalizes explicit release provenance and writes the shared manifest", () => {
|
|
const rootDir = createPackage();
|
|
const execFileSync = vi.fn(() => {
|
|
throw new Error("Git fallback should not run");
|
|
});
|
|
|
|
const outputPath = writeBuildInfo({
|
|
rootDir,
|
|
env: {
|
|
GIT_COMMIT: "ABCDEF0123456789ABCDEF0123456789ABCDEF01",
|
|
OPENCLAW_BUILD_TIMESTAMP: "2026-07-10T12:34:56Z",
|
|
},
|
|
execFileSync,
|
|
});
|
|
|
|
expect(execFileSync).not.toHaveBeenCalled();
|
|
expect(path.relative(rootDir, outputPath)).toBe("dist/build-info.json");
|
|
expect(JSON.parse(fs.readFileSync(outputPath, "utf8"))).toEqual({
|
|
version: "2026.7.10",
|
|
commit: "abcdef0123456789abcdef0123456789abcdef01",
|
|
builtAt: "2026-07-10T12:34:56.000Z",
|
|
});
|
|
});
|
|
|
|
it("falls back to build-time Git and one current UTC timestamp for local builds", () => {
|
|
const rootDir = createPackage("2026.7.10-beta.1");
|
|
const execFileSync = vi.fn(() => "1234567890ABCDEF1234567890ABCDEF12345678\n");
|
|
|
|
expect(
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: {},
|
|
execFileSync,
|
|
now: () => new Date("2026-07-10T01:02:03.456Z"),
|
|
}),
|
|
).toEqual({
|
|
version: "2026.7.10-beta.1",
|
|
commit: "1234567890abcdef1234567890abcdef12345678",
|
|
builtAt: "2026-07-10T01:02:03.456Z",
|
|
});
|
|
expect(execFileSync).toHaveBeenCalledWith("git", ["rev-parse", "HEAD"], {
|
|
cwd: rootDir,
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
});
|
|
});
|
|
|
|
it("uses null when Git metadata is unavailable", () => {
|
|
const rootDir = createPackage();
|
|
|
|
expect(
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: {},
|
|
execFileSync: () => {
|
|
throw new Error("git unavailable");
|
|
},
|
|
now: () => new Date("2026-07-10T01:02:03.000Z"),
|
|
}).commit,
|
|
).toBeNull();
|
|
});
|
|
|
|
it("preserves GIT_COMMIT then GIT_SHA explicit input precedence", () => {
|
|
const rootDir = createPackage();
|
|
const fallbackSha = "1234567890abcdef1234567890abcdef12345678";
|
|
|
|
expect(
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: { GIT_SHA: fallbackSha },
|
|
now: () => new Date("2026-07-10T01:02:03.000Z"),
|
|
}).commit,
|
|
).toBe(fallbackSha);
|
|
expect(() =>
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: { GIT_COMMIT: "bad", GIT_SHA: fallbackSha },
|
|
}),
|
|
).toThrow("GIT_COMMIT must be a full 40-character Git commit SHA.");
|
|
});
|
|
|
|
it("uses checked-out Git instead of unverified GitHub workflow context", () => {
|
|
const rootDir = createPackage();
|
|
const checkedOutCommit = "b".repeat(40);
|
|
const execFileSync = vi.fn(() => checkedOutCommit);
|
|
|
|
expect(
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: { GITHUB_SHA: "a".repeat(40) },
|
|
execFileSync,
|
|
now: () => new Date("2026-07-10T01:02:03.000Z"),
|
|
}).commit,
|
|
).toBe(checkedOutCommit);
|
|
expect(execFileSync).toHaveBeenCalledOnce();
|
|
expect(
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: { GITHUB_SHA: "a".repeat(40) },
|
|
execFileSync: () => {
|
|
throw new Error("git unavailable");
|
|
},
|
|
now: () => new Date("2026-07-10T01:02:03.000Z"),
|
|
}).commit,
|
|
).toBe("a".repeat(40));
|
|
expect(() =>
|
|
resolveBuildInfo({
|
|
rootDir,
|
|
env: { GITHUB_SHA: "bad" },
|
|
execFileSync: () => {
|
|
throw new Error("git unavailable");
|
|
},
|
|
}),
|
|
).toThrow("GITHUB_SHA must be a full 40-character Git commit SHA.");
|
|
});
|
|
|
|
it("rejects abbreviated or malformed explicit commits", () => {
|
|
expect(() => normalizeBuildCommit("abc1234")).toThrow(
|
|
"GIT_COMMIT must be a full 40-character Git commit SHA.",
|
|
);
|
|
expect(() => normalizeBuildCommit("g".repeat(40))).toThrow(
|
|
"GIT_COMMIT must be a full 40-character Git commit SHA.",
|
|
);
|
|
});
|
|
|
|
it("normalizes valid UTC timestamps and rejects offsets or impossible dates", () => {
|
|
expect(normalizeBuildTimestamp("2026-07-10T12:34:56.7Z")).toBe("2026-07-10T12:34:56.700Z");
|
|
expect(() => normalizeBuildTimestamp("2026-07-10T12:34:56+00:00")).toThrow(
|
|
"OPENCLAW_BUILD_TIMESTAMP must be an ISO-8601 UTC timestamp ending in Z.",
|
|
);
|
|
expect(() => normalizeBuildTimestamp("2026-02-30T12:34:56Z")).toThrow(
|
|
"OPENCLAW_BUILD_TIMESTAMP must be a valid ISO-8601 UTC timestamp.",
|
|
);
|
|
});
|
|
});
|