mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
perf: speed up launcher version output
This commit is contained in:
153
openclaw.mjs
153
openclaw.mjs
@@ -41,6 +41,10 @@ const ensureSupportedNodeVersion = () => {
|
|||||||
|
|
||||||
ensureSupportedNodeVersion();
|
ensureSupportedNodeVersion();
|
||||||
|
|
||||||
|
if (tryOutputLauncherVersion(process.argv)) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const isSourceCheckoutLauncher = () =>
|
const isSourceCheckoutLauncher = () =>
|
||||||
existsSync(new URL("./.git", import.meta.url)) ||
|
existsSync(new URL("./.git", import.meta.url)) ||
|
||||||
existsSync(new URL("./src/entry.ts", import.meta.url));
|
existsSync(new URL("./src/entry.ts", import.meta.url));
|
||||||
@@ -445,6 +449,155 @@ const loadPrecomputedHelpText = (key) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function tryOutputLauncherVersion(argv) {
|
||||||
|
try {
|
||||||
|
if (normalizeLauncherMetadataValue(process.env.OPENCLAW_CONTAINER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isLauncherVersionFastPathArgv(argv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const version = resolveLauncherVersion();
|
||||||
|
const commit = resolveLauncherCommit();
|
||||||
|
process.stdout.write(commit ? `OpenClaw ${version} (${commit})\n` : `OpenClaw ${version}\n`);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLauncherVersionFastPathArgv(argv) {
|
||||||
|
return argv.length === 3 && (argv[2] === "--version" || argv[2] === "-V" || argv[2] === "-v");
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLauncherMetadataValue(value) {
|
||||||
|
const trimmed = typeof value === "string" ? value.trim() : "";
|
||||||
|
return trimmed && trimmed !== "undefined" && trimmed !== "null" ? trimmed : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readLauncherJson(relativePath) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(readFileSync(new URL(relativePath, import.meta.url), "utf8"));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLauncherVersion() {
|
||||||
|
const packageJson = readLauncherJson("./package.json");
|
||||||
|
const packageVersion = normalizeLauncherMetadataValue(packageJson?.version);
|
||||||
|
if (packageVersion) {
|
||||||
|
return packageVersion;
|
||||||
|
}
|
||||||
|
const buildInfo = readLauncherJson("./dist/build-info.json");
|
||||||
|
const buildVersion = normalizeLauncherMetadataValue(buildInfo?.version);
|
||||||
|
if (buildVersion) {
|
||||||
|
return buildVersion;
|
||||||
|
}
|
||||||
|
return normalizeLauncherMetadataValue(process.env.OPENCLAW_BUNDLED_VERSION) ?? "0.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLauncherCommit() {
|
||||||
|
const envCommit = formatLauncherCommit(process.env.GIT_COMMIT ?? process.env.GIT_SHA);
|
||||||
|
if (envCommit) {
|
||||||
|
return envCommit;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
readLauncherGitCommit() ??
|
||||||
|
formatLauncherCommit(readLauncherJson("./dist/build-info.json")?.commit) ??
|
||||||
|
formatLauncherCommit(readLauncherJson("./package.json")?.gitHead) ??
|
||||||
|
formatLauncherCommit(readLauncherJson("./package.json")?.githead)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLauncherCommit(value) {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const match = value.trim().match(/[0-9a-fA-F]{7,40}/);
|
||||||
|
return match ? match[0].slice(0, 7).toLowerCase() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readLauncherGitCommit() {
|
||||||
|
try {
|
||||||
|
const gitPath = fileURLToPath(new URL("./.git", import.meta.url));
|
||||||
|
const headPath = resolveLauncherGitHeadPath(gitPath);
|
||||||
|
if (!headPath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const head = readFileSync(headPath, "utf8").trim();
|
||||||
|
if (!head) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!head.startsWith("ref:")) {
|
||||||
|
return formatLauncherCommit(head);
|
||||||
|
}
|
||||||
|
const ref = head.replace(/^ref:\s*/i, "").trim();
|
||||||
|
if (!ref.startsWith("refs/") || path.isAbsolute(ref) || ref.split("/").includes("..")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const refsBase = resolveLauncherGitRefsBase(headPath);
|
||||||
|
const refPath = path.resolve(refsBase, ref);
|
||||||
|
const rel = path.relative(refsBase, refPath);
|
||||||
|
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return formatLauncherCommit(readFileSync(refPath, "utf8"));
|
||||||
|
} catch {
|
||||||
|
return readLauncherPackedRef(refsBase, ref);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLauncherGitHeadPath(gitPath) {
|
||||||
|
try {
|
||||||
|
if (statSync(gitPath).isDirectory()) {
|
||||||
|
return path.join(gitPath, "HEAD");
|
||||||
|
}
|
||||||
|
const raw = readFileSync(gitPath, "utf8").trim();
|
||||||
|
if (!raw.startsWith("gitdir:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return path.join(
|
||||||
|
path.resolve(path.dirname(gitPath), raw.slice("gitdir:".length).trim()),
|
||||||
|
"HEAD",
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLauncherGitRefsBase(headPath) {
|
||||||
|
const gitDir = path.dirname(headPath);
|
||||||
|
try {
|
||||||
|
const commonDir = readFileSync(path.join(gitDir, "commondir"), "utf8").trim();
|
||||||
|
return commonDir ? path.resolve(gitDir, commonDir) : gitDir;
|
||||||
|
} catch {
|
||||||
|
return gitDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readLauncherPackedRef(refsBase, ref) {
|
||||||
|
try {
|
||||||
|
const packedRefs = readFileSync(path.join(refsBase, "packed-refs"), "utf8");
|
||||||
|
for (const line of packedRefs.split("\n")) {
|
||||||
|
if (!line || line.startsWith("#") || line.startsWith("^")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const [commit, packedRef] = line.trim().split(/\s+/, 2);
|
||||||
|
if (packedRef === ref) {
|
||||||
|
return formatLauncherCommit(commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const tryOutputBareRootHelp = async () => {
|
const tryOutputBareRootHelp = async () => {
|
||||||
if (!isBareRootHelpInvocation(process.argv)) {
|
if (!isBareRootHelpInvocation(process.argv)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -231,6 +231,78 @@ describe("openclaw launcher", () => {
|
|||||||
expect(result.stderr).toContain("missing dist/entry.(m)js");
|
expect(result.stderr).toContain("missing dist/entry.(m)js");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prints root version without importing the runtime entry", async () => {
|
||||||
|
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixtureRoot, "package.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
name: "openclaw",
|
||||||
|
version: "1.2.3-test",
|
||||||
|
gitHead: "abcdef0123456789",
|
||||||
|
}),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixtureRoot, "dist", "entry.js"),
|
||||||
|
"throw new Error('runtime entry should not load for --version');\n",
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = spawnSync(
|
||||||
|
process.execPath,
|
||||||
|
[path.join(fixtureRoot, "openclaw.mjs"), "--version"],
|
||||||
|
{
|
||||||
|
cwd: fixtureRoot,
|
||||||
|
env: launcherEnv(),
|
||||||
|
encoding: "utf8",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(result.stdout).toBe("OpenClaw 1.2.3-test (abcdef0)\n");
|
||||||
|
expect(result.stderr).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defers container-targeted root version to the runtime entry", async () => {
|
||||||
|
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixtureRoot, "package.json"),
|
||||||
|
JSON.stringify({ name: "openclaw", version: "1.2.3-test" }),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixtureRoot, "dist", "entry.js"),
|
||||||
|
"process.stdout.write('RUNTIME ENTRY\\n');\n",
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = spawnSync(
|
||||||
|
process.execPath,
|
||||||
|
[path.join(fixtureRoot, "openclaw.mjs"), "--container", "demo", "--version"],
|
||||||
|
{
|
||||||
|
cwd: fixtureRoot,
|
||||||
|
env: launcherEnv(),
|
||||||
|
encoding: "utf8",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(result.stdout).toBe("RUNTIME ENTRY\n");
|
||||||
|
|
||||||
|
const envResult = spawnSync(
|
||||||
|
process.execPath,
|
||||||
|
[path.join(fixtureRoot, "openclaw.mjs"), "--version"],
|
||||||
|
{
|
||||||
|
cwd: fixtureRoot,
|
||||||
|
env: launcherEnv({ OPENCLAW_CONTAINER: "demo" }),
|
||||||
|
encoding: "utf8",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(envResult.status).toBe(0);
|
||||||
|
expect(envResult.stdout).toBe("RUNTIME ENTRY\n");
|
||||||
|
});
|
||||||
|
|
||||||
it("treats Bun direct optional import misses as direct launcher misses", async () => {
|
it("treats Bun direct optional import misses as direct launcher misses", async () => {
|
||||||
const fixtureRoot = await makeLauncherProbeFixture(
|
const fixtureRoot = await makeLauncherProbeFixture(
|
||||||
fixtureRoots,
|
fixtureRoots,
|
||||||
|
|||||||
Reference in New Issue
Block a user