mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 00:50:27 +00:00
fix(ci): write dist build stamp after builds
This commit is contained in:
@@ -586,10 +586,10 @@
|
||||
"android:test": "cd apps/android && ./gradlew :app:testPlayDebugUnitTest",
|
||||
"android:test:integration": "OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_ANDROID_NODE=1 vitest run --config vitest.live.config.ts src/gateway/android-node.capabilities.live.test.ts",
|
||||
"android:test:third-party": "cd apps/android && ./gradlew :app:testThirdPartyDebugUnitTest",
|
||||
"build": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
|
||||
"build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
|
||||
"build": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
|
||||
"build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
|
||||
"build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json",
|
||||
"build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts",
|
||||
"build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && pnpm build:plugin-sdk:dts",
|
||||
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
|
||||
"check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm check:base-config-schema && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-src-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:plugins:no-extension-imports && pnpm lint:plugins:plugin-sdk-subpaths-exported && pnpm lint:extensions:no-src-outside-plugin-sdk && pnpm lint:extensions:no-plugin-sdk-internal && pnpm lint:extensions:no-relative-outside-package && pnpm lint:web-search-provider-boundaries && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",
|
||||
"check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check",
|
||||
|
||||
22
scripts/build-stamp.d.mts
Normal file
22
scripts/build-stamp.d.mts
Normal file
@@ -0,0 +1,22 @@
|
||||
export function resolveGitHead(params?: {
|
||||
cwd?: string;
|
||||
spawnSync?: (
|
||||
cmd: string,
|
||||
args: string[],
|
||||
options: unknown,
|
||||
) => { status: number | null; stdout?: string | null };
|
||||
}): string | null;
|
||||
|
||||
export function writeBuildStamp(params?: {
|
||||
cwd?: string;
|
||||
fs?: {
|
||||
mkdirSync(path: string, options?: { recursive?: boolean }): void;
|
||||
writeFileSync(path: string, data: string, encoding?: string): void;
|
||||
};
|
||||
now?: () => number;
|
||||
spawnSync?: (
|
||||
cmd: string,
|
||||
args: string[],
|
||||
options: unknown,
|
||||
) => { status: number | null; stdout?: string | null };
|
||||
}): string;
|
||||
50
scripts/build-stamp.mjs
Normal file
50
scripts/build-stamp.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
export function resolveGitHead(params = {}) {
|
||||
const cwd = params.cwd ?? process.cwd();
|
||||
const spawnSyncImpl = params.spawnSync ?? spawnSync;
|
||||
try {
|
||||
const result = spawnSyncImpl("git", ["rev-parse", "HEAD"], {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
return null;
|
||||
}
|
||||
const head = (result.stdout ?? "").trim();
|
||||
return head || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeBuildStamp(params = {}) {
|
||||
const cwd = params.cwd ?? process.cwd();
|
||||
const fsImpl = params.fs ?? fs;
|
||||
const now = params.now ?? Date.now;
|
||||
const distRoot = path.join(cwd, "dist");
|
||||
const buildStampPath = path.join(distRoot, ".buildstamp");
|
||||
const head = resolveGitHead({
|
||||
cwd,
|
||||
spawnSync: params.spawnSync,
|
||||
});
|
||||
|
||||
fsImpl.mkdirSync(distRoot, { recursive: true });
|
||||
fsImpl.writeFileSync(buildStampPath, `${JSON.stringify({ builtAt: now(), head })}\n`, "utf8");
|
||||
return buildStampPath;
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
try {
|
||||
writeBuildStamp();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { resolveGitHead, writeBuildStamp as writeDistBuildStamp } from "./build-stamp.mjs";
|
||||
import { runRuntimePostBuild } from "./runtime-postbuild.mjs";
|
||||
|
||||
const buildScript = "scripts/tsdown-build.mjs";
|
||||
@@ -121,27 +122,6 @@ const findLatestMtime = (dirPath, shouldSkip, deps) => {
|
||||
return latest;
|
||||
};
|
||||
|
||||
const runGit = (gitArgs, deps) => {
|
||||
try {
|
||||
const result = deps.spawnSync("git", gitArgs, {
|
||||
cwd: deps.cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
return null;
|
||||
}
|
||||
return (result.stdout ?? "").trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const resolveGitHead = (deps) => {
|
||||
const head = runGit(["rev-parse", "HEAD"], deps);
|
||||
return head || null;
|
||||
};
|
||||
|
||||
const readGitStatus = (deps) => {
|
||||
try {
|
||||
const result = deps.spawnSync(
|
||||
@@ -291,12 +271,11 @@ const syncRuntimeArtifacts = (deps) => {
|
||||
|
||||
const writeBuildStamp = (deps) => {
|
||||
try {
|
||||
deps.fs.mkdirSync(deps.distRoot, { recursive: true });
|
||||
const stamp = {
|
||||
builtAt: Date.now(),
|
||||
head: resolveGitHead(deps),
|
||||
};
|
||||
deps.fs.writeFileSync(deps.buildStampPath, `${JSON.stringify(stamp)}\n`);
|
||||
writeDistBuildStamp({
|
||||
cwd: deps.cwd,
|
||||
fs: deps.fs,
|
||||
spawnSync: deps.spawnSync,
|
||||
});
|
||||
} catch (error) {
|
||||
// Best-effort stamp; still allow the runner to start.
|
||||
logRunner(`Failed to write build stamp: ${error?.message ?? "unknown error"}`, deps);
|
||||
|
||||
35
src/infra/build-stamp.test.ts
Normal file
35
src/infra/build-stamp.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { writeBuildStamp } from "../../scripts/build-stamp.mjs";
|
||||
|
||||
async function withTempDir<T>(run: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-build-stamp-"));
|
||||
try {
|
||||
return await run(dir);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe("build-stamp script", () => {
|
||||
it("writes dist/.buildstamp with the current git head", async () => {
|
||||
await withTempDir(async (tmp) => {
|
||||
const stampPath = writeBuildStamp({
|
||||
cwd: tmp,
|
||||
now: () => 1_700_000_000_000,
|
||||
spawnSync: (cmd: string, args: string[]) => {
|
||||
if (cmd === "git" && args[0] === "rev-parse") {
|
||||
return { status: 0, stdout: "abc123\n" };
|
||||
}
|
||||
return { status: 1, stdout: "" };
|
||||
},
|
||||
});
|
||||
|
||||
await expect(fs.readFile(stampPath, "utf8")).resolves.toBe(
|
||||
'{"builtAt":1700000000000,"head":"abc123"}\n',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user