Files
kit.program/kit/src/remote/install.js
T

85 lines
3.4 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");
function now() {
return new Date().toISOString();
}
function verifyChecksum(filePath, checksum) {
if (!checksum) {
return;
}
const [alg, expected] = checksum.split(":", 2);
if (!alg || !expected) {
return;
}
const crypto = require("crypto");
const actual = crypto.createHash(alg).update(fs.readFileSync(filePath)).digest("hex");
if (actual !== expected) {
throw new Error(`文件校验失败 ${filePath}`);
}
}
function unpackArtifact(artifact, localPath, versionDir) {
if (artifact.type !== "archive" || !artifact.unpack) {
return;
}
const extractDir = path.resolve(path.dirname(localPath), "_extracted");
fs.mkdirSync(extractDir, { recursive: true });
const format = artifact.archive_format || "zip";
if (format === "zip") {
if (process.platform === "win32") {
const source = localPath.replace(/'/g, "''");
const target = extractDir.replace(/'/g, "''");
execFileSync("powershell.exe", ["-NoProfile", "-Command", `Expand-Archive -Path '${source}' -DestinationPath '${target}' -Force`], { stdio: "inherit" });
} else {
execFileSync("unzip", ["-o", localPath, "-d", extractDir], { stdio: "inherit" });
}
} else if (["tar", "tar.gz", "tgz", "tar.xz"].includes(format)) {
execFileSync("tar", ["-xf", localPath, "-C", extractDir], { stdio: "inherit" });
}
artifact.extracted_path = path.relative(versionDir, extractDir).replace(/\\/g, "/");
if (artifact.entry_point) {
artifact.extracted_entry_point = path.posix.join(artifact.extracted_path, artifact.entry_point.replace(/\\/g, "/"));
}
}
module.exports = async function install(remote, id) {
const parsed = remote.parseProjectVersion(id || "kit");
const projectId = parsed.projectId;
const group = parsed.group || "/modules";
const project = await remote.getProjectMetadata(projectId, group);
const version = parsed.version || project.version;
if (!version) {
throw new Error(`远程模块没有可安装版本 ${projectId}`);
}
const versionMeta = await remote.getVersionMetadata(projectId, version, project.group || group);
const artifacts = remote.getMatchingArtifacts(versionMeta);
if (artifacts.length < 1) {
throw new Error(`没有匹配当前平台的文件 ${projectId}@${version}`);
}
const root = remote.getLocalModuleRoot(projectId);
const versionDir = path.resolve(root, "versions", version);
fs.mkdirSync(versionDir, { recursive: true });
for (const artifact of artifacts) {
const localPath = await remote.downloadArtifact(projectId, version, artifact, versionDir, project.group || group);
verifyChecksum(localPath, artifact.checksum || (versionMeta.checksums && versionMeta.checksums[artifact.path]));
unpackArtifact(artifact, localPath, versionDir);
}
const localVersionMeta = Object.assign({}, versionMeta, { installed_at: now(), artifacts });
fs.writeFileSync(path.resolve(versionDir, ".version.json"), JSON.stringify(localVersionMeta, null, 4), "utf-8");
const state = remote.getInstallState(projectId);
const action = state.current_version ? "switch" : "install";
await remote.activate(projectId, version, action);
$logger.info(`当前版本 ${projectId}@${version}`);
return version;
};