openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
156 lines (155 loc) • 5.77 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CNp1Ofdk.js";
import { n as resolveGitHeadPath } from "./git-root-B5CYl5JZ.js";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import path from "node:path";
//#region src/infra/git-commit.ts
const formatCommit = (value) => {
if (!value) return null;
const trimmed = value.trim();
if (!trimmed) return null;
const match = trimmed.match(/[0-9a-fA-F]{7,40}/);
if (!match) return null;
return normalizeLowercaseStringOrEmpty(match[0].slice(0, 7));
};
const cachedGitCommitBySearchDir = /* @__PURE__ */ new Map();
function isMissingPathError(error) {
if (!(error instanceof Error)) return false;
const code = error.code;
return code === "ENOENT" || code === "ENOTDIR";
}
const resolveCommitSearchDir = (options) => {
if (options.cwd) return path.resolve(options.cwd);
if (options.moduleUrl) try {
return path.dirname(fileURLToPath(options.moduleUrl));
} catch {}
return process.cwd();
};
/** Read at most `limit` bytes from a file to avoid unbounded reads. */
const safeReadFilePrefix = (filePath, limit = 256) => {
const fd = fs.openSync(filePath, "r");
try {
const buf = Buffer.alloc(limit);
const bytesRead = fs.readSync(fd, buf, 0, limit, 0);
return buf.subarray(0, bytesRead).toString("utf-8");
} finally {
fs.closeSync(fd);
}
};
const cacheGitCommit = (searchDir, commit) => {
cachedGitCommitBySearchDir.set(searchDir, commit);
return commit;
};
const clearCachedGitCommits = () => {
cachedGitCommitBySearchDir.clear();
};
const resolveGitLookupDepth = (searchDir, packageRoot) => {
if (!packageRoot) return;
const relative = path.relative(packageRoot, searchDir);
if (relative.startsWith("..") || path.isAbsolute(relative)) return;
return (relative ? relative.split(path.sep).filter(Boolean).length : 0) + 1;
};
const readCommitFromGit = (searchDir, packageRoot) => {
const headPath = resolveGitHeadPath(searchDir, { maxDepth: resolveGitLookupDepth(searchDir, packageRoot) });
if (!headPath) return;
const head = fs.readFileSync(headPath, "utf-8").trim();
if (!head) return null;
if (head.startsWith("ref:")) {
const ref = head.replace(/^ref:\s*/i, "").trim();
const refsBase = resolveGitRefsBase(headPath);
const refPath = resolveRefPath(refsBase, ref);
if (!refPath) return null;
try {
return formatCommit(safeReadFilePrefix(refPath).trim());
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
return readCommitFromPackedRefs(refsBase, ref);
}
return formatCommit(head);
};
const resolveGitRefsBase = (headPath) => {
const gitDir = path.dirname(headPath);
try {
const commonDir = safeReadFilePrefix(path.join(gitDir, "commondir")).trim();
if (commonDir) return path.resolve(gitDir, commonDir);
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
return gitDir;
};
const readCommitFromPackedRefs = (refsBase, ref) => {
try {
const packedRefs = fs.readFileSync(path.join(refsBase, "packed-refs"), "utf-8");
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 formatCommit(commit);
}
return null;
} catch (error) {
if (!isMissingPathError(error)) throw error;
return null;
}
};
/** Safely resolve a git ref path, rejecting traversal attacks from a crafted HEAD file. */
const resolveRefPath = (refsBase, ref) => {
if (!ref.startsWith("refs/")) return null;
if (path.isAbsolute(ref)) return null;
if (ref.split(/[/]/).includes("..")) return null;
const resolved = path.resolve(refsBase, ref);
const rel = path.relative(refsBase, resolved);
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null;
return resolved;
};
const readCommitFromPackageJson = () => {
try {
const pkg = createRequire(import.meta.url)("../../package.json");
return formatCommit(pkg.gitHead ?? pkg.githead ?? null);
} catch {
return null;
}
};
const readCommitFromBuildInfo = () => {
try {
const require = createRequire(import.meta.url);
for (const candidate of ["../build-info.json", "./build-info.json"]) try {
const formatted = formatCommit(require(candidate).commit ?? null);
if (formatted) return formatted;
} catch {}
return null;
} catch {
return null;
}
};
const resolveCommitHash = (options = {}) => {
const env = options.env ?? process.env;
const readers = options.readers ?? {};
const readGitCommit = readers.readGitCommit ?? readCommitFromGit;
const normalized = formatCommit(env.GIT_COMMIT?.trim() || env.GIT_SHA?.trim());
if (normalized) return normalized;
const searchDir = resolveCommitSearchDir(options);
if (cachedGitCommitBySearchDir.has(searchDir)) return cachedGitCommitBySearchDir.get(searchDir) ?? null;
const packageRoot = resolveOpenClawPackageRootSync({
cwd: options.cwd,
moduleUrl: options.moduleUrl
});
try {
const gitCommit = readGitCommit(searchDir, packageRoot);
if (gitCommit !== void 0) return cacheGitCommit(searchDir, gitCommit);
} catch {}
const buildInfoCommit = readers.readBuildInfoCommit?.() ?? readCommitFromBuildInfo();
if (buildInfoCommit) return cacheGitCommit(searchDir, buildInfoCommit);
const pkgCommit = readers.readPackageJsonCommit?.() ?? readCommitFromPackageJson();
if (pkgCommit) return cacheGitCommit(searchDir, pkgCommit);
try {
return cacheGitCommit(searchDir, readGitCommit(searchDir, packageRoot) ?? null);
} catch {
return cacheGitCommit(searchDir, null);
}
};
const testing = { clearCachedGitCommits };
//#endregion
export { testing as n, resolveCommitHash as t };