85 lines
3.4 KiB
JavaScript
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;
|
|
};
|