openclaw/scripts/e2e/lib/package-git-fixture.mjs
Peter Steinberger 8b16e5328e
fix: prevent temporary-file exhaustion from SQLite coordination (#157413)
Use transactions on the actual SQLite state and device databases for ordinary writes. Remove redundant coordination databases, transport, and exclusion layers while preserving bounded process ownership for startup, schema work, and offline maintenance.

Tie test and QA scratch retirement to settled workers and native resources, preserve active plugin captures, and join SDK declaration compiler processes before synchronous semantic rendering.

Validation: main-tier CI on 5e731c1f64 had 144 successful jobs and one Windows ACP initialization timeout. Qualified unchanged replay 36314027585 passed all 896 tests with the original 48-file order, six projects, toolchain, and deadlines. The original timeout remains unexplained and recorded in the PR. Reviewed main-conflict integration through 39caa592ef passes focused SQLite, Doctor, image, and Cron proof plus affected typechecks and lint. No accepted actionable independent-review findings remain.

Squash landing of #157413 under explicit maintainer authority to resolve logical main drift and admin-merge using the completed CI evidence. No PR-specific schema or public configuration migration.
2026-09-27 05:30:41 -07:00

102 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
// Prepares package-derived Docker E2E fixtures for git-style npm installs.
import fs from "node:fs";
import path from "node:path";
import { PACKAGE_LIFECYCLE_PENDING_RELATIVE_PATH } from "../../lib/package-lifecycle-marker.mjs";
const [command, rootArg] = process.argv.slice(2);
function usage() {
console.error("usage: package-git-fixture.mjs prepare <fixture-root>");
process.exit(2);
}
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function writeJson(file, value) {
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
}
function withoutAiRuntimeDependency(value) {
if (!Array.isArray(value)) {
return value;
}
const next = value.filter((entry) => entry !== "@openclaw/ai");
return next.length > 0 ? next : undefined;
}
function ensureDependencyIgnores(root) {
const gitignorePath = path.join(root, ".gitignore");
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8") : "";
const lines = new Set(existing.split(/\r?\n/u));
const required = [
"node_modules",
"**/node_modules/",
"pnpm-lock.yaml",
// Match source checkouts while the updater holds its runtime artifact owner.
".artifacts/",
// Runtime promotion stages destination siblings before its final clean check.
"*.openclaw-update-*.tmp/",
PACKAGE_LIFECYCLE_PENDING_RELATIVE_PATH,
];
const missing = required.filter((entry) => !lines.has(entry));
if (missing.length === 0) {
return;
}
const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}\n`;
fs.writeFileSync(gitignorePath, `${prefix}${missing.join("\n")}\n`);
}
function prepare(root) {
ensureDependencyIgnores(root);
// Preserve source-checkout identity through preflight clones and global links;
// packaged postinstall cleanup would otherwise delete the fixture's build stamps.
for (const directory of ["src", "extensions"]) {
fs.mkdirSync(path.join(root, directory), { recursive: true });
fs.writeFileSync(path.join(root, directory, ".gitkeep"), "");
}
const packageJsonPath = path.join(root, "package.json");
const packageJson = readJson(packageJsonPath);
// npm still resolves omitted dev dependencies; this fixture runs the packed runtime.
delete packageJson.devDependencies;
packageJson.scripts = {
...packageJson.scripts,
openclaw: "node openclaw.mjs",
};
delete packageJson.scripts.postinstall;
const aiRuntimeSource = path.join(root, "node_modules", "@openclaw", "ai");
const aiRuntimePackageJson = path.join(aiRuntimeSource, "package.json");
if (!fs.existsSync(aiRuntimePackageJson)) {
writeJson(packageJsonPath, packageJson);
return;
}
const aiRuntimeTarget = path.join(root, ".openclaw-fixture", "packages", "ai");
fs.rmSync(aiRuntimeTarget, { force: true, recursive: true });
fs.mkdirSync(path.dirname(aiRuntimeTarget), { recursive: true });
fs.renameSync(aiRuntimeSource, aiRuntimeTarget);
const relocatedAiRuntimePackageJson = path.join(aiRuntimeTarget, "package.json");
const relocatedAiRuntimePackage = readJson(relocatedAiRuntimePackageJson);
delete relocatedAiRuntimePackage.devDependencies;
writeJson(relocatedAiRuntimePackageJson, relocatedAiRuntimePackage);
packageJson.dependencies ??= {};
packageJson.dependencies["@openclaw/ai"] = "file:.openclaw-fixture/packages/ai";
packageJson.bundleDependencies = withoutAiRuntimeDependency(packageJson.bundleDependencies);
packageJson.bundledDependencies = withoutAiRuntimeDependency(packageJson.bundledDependencies);
if (packageJson.bundleDependencies === undefined) {
delete packageJson.bundleDependencies;
}
if (packageJson.bundledDependencies === undefined) {
delete packageJson.bundledDependencies;
}
writeJson(packageJsonPath, packageJson);
}
if (command !== "prepare" || !rootArg) {
usage();
}
prepare(path.resolve(rootArg));