69 lines
2.0 KiB
TypeScript
69 lines
2.0 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import { runCommandWithTimeout } from "../process/exec.js";
|
|
import { fileExists } from "./archive.js";
|
|
|
|
export async function installPackageDir(params: {
|
|
sourceDir: string;
|
|
targetDir: string;
|
|
mode: "install" | "update";
|
|
timeoutMs: number;
|
|
logger?: { info?: (message: string) => void };
|
|
copyErrorPrefix: string;
|
|
hasDeps: boolean;
|
|
depsLogMessage: string;
|
|
afterCopy?: () => void | Promise<void>;
|
|
}): Promise<{ ok: true } | { ok: false; error: string }> {
|
|
params.logger?.info?.(`Installing to ${params.targetDir}…`);
|
|
let backupDir: string | null = null;
|
|
if (params.mode === "update" && (await fileExists(params.targetDir))) {
|
|
backupDir = `${params.targetDir}.backup-${Date.now()}`;
|
|
await fs.rename(params.targetDir, backupDir);
|
|
}
|
|
|
|
const rollback = async () => {
|
|
if (!backupDir) {
|
|
return;
|
|
}
|
|
await fs.rm(params.targetDir, { recursive: true, force: true }).catch(() => undefined);
|
|
await fs.rename(backupDir, params.targetDir).catch(() => undefined);
|
|
};
|
|
|
|
try {
|
|
await fs.cp(params.sourceDir, params.targetDir, { recursive: true });
|
|
} catch (err) {
|
|
await rollback();
|
|
return { ok: false, error: `${params.copyErrorPrefix}: ${String(err)}` };
|
|
}
|
|
|
|
try {
|
|
await params.afterCopy?.();
|
|
} catch (err) {
|
|
await rollback();
|
|
return { ok: false, error: `post-copy validation failed: ${String(err)}` };
|
|
}
|
|
|
|
if (params.hasDeps) {
|
|
params.logger?.info?.(params.depsLogMessage);
|
|
const npmRes = await runCommandWithTimeout(
|
|
["npm", "install", "--omit=dev", "--silent", "--ignore-scripts"],
|
|
{
|
|
timeoutMs: Math.max(params.timeoutMs, 300_000),
|
|
cwd: params.targetDir,
|
|
},
|
|
);
|
|
if (npmRes.code !== 0) {
|
|
await rollback();
|
|
return {
|
|
ok: false,
|
|
error: `npm install failed: ${npmRes.stderr.trim() || npmRes.stdout.trim()}`,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (backupDir) {
|
|
await fs.rm(backupDir, { recursive: true, force: true }).catch(() => undefined);
|
|
}
|
|
|
|
return { ok: true };
|
|
}
|