perf: speed up launcher version output

This commit is contained in:
Peter Steinberger
2026-05-29 00:41:32 +01:00
parent 9a4aa438bb
commit 0e40408375
2 changed files with 225 additions and 0 deletions

View File

@@ -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;

View File

@@ -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,