2023-03-07 16:43:12 +01:00
|
|
|
import { exec as execCallback } from "child_process";
|
2023-03-29 09:54:29 +02:00
|
|
|
import { createHash } from "crypto";
|
|
|
|
import { mkdir, stat, writeFile } from "fs/promises";
|
|
|
|
import fetch, { type FetchOptions } from "make-fetch-happen";
|
|
|
|
import { dirname as pathDirname, join as pathJoin } from "path";
|
|
|
|
import { assert } from "tsafe";
|
2023-03-07 16:43:12 +01:00
|
|
|
import { promisify } from "util";
|
2023-03-29 09:54:29 +02:00
|
|
|
import { getProjectRoot } from "./getProjectRoot";
|
|
|
|
import { transformCodebase } from "./transformCodebase";
|
|
|
|
import { unzip } from "./unzip";
|
2023-03-07 16:43:12 +01:00
|
|
|
|
|
|
|
const exec = promisify(execCallback);
|
2021-02-28 18:40:57 +01:00
|
|
|
|
2023-01-16 14:42:20 +01:00
|
|
|
function hash(s: string) {
|
|
|
|
return createHash("sha256").update(s).digest("hex");
|
|
|
|
}
|
2021-03-03 02:31:02 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
async function exists(path: string) {
|
2023-01-16 14:42:20 +01:00
|
|
|
try {
|
2023-03-29 09:54:29 +02:00
|
|
|
await stat(path);
|
|
|
|
return true;
|
2023-01-16 14:42:20 +01:00
|
|
|
} catch (error) {
|
2023-03-29 09:54:29 +02:00
|
|
|
if ((error as Error & { code: string }).code === "ENOENT") return false;
|
2023-01-16 14:42:20 +01:00
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
2021-10-06 17:22:52 +02:00
|
|
|
|
2023-01-16 14:42:20 +01:00
|
|
|
/**
|
2023-03-29 09:54:29 +02:00
|
|
|
* Get npm configuration as map
|
2023-03-07 16:43:12 +01:00
|
|
|
*/
|
2023-03-29 09:54:29 +02:00
|
|
|
async function getNmpConfig(): Promise<Record<string, string>> {
|
|
|
|
const { stdout } = await exec("npm config get", { encoding: "utf8" });
|
|
|
|
return stdout
|
|
|
|
.split("\n")
|
|
|
|
.filter(line => !line.startsWith(";"))
|
|
|
|
.map(line => line.trim())
|
|
|
|
.map(line => line.split("=", 2))
|
|
|
|
.reduce((cfg, [key, value]) => ({ ...cfg, [key]: value }), {});
|
2023-03-07 16:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get proxy configuration from npm config files. Note that we don't care about
|
|
|
|
* proxy config in env vars, because make-fetch-happen will do that for us.
|
|
|
|
*
|
|
|
|
* @returns proxy configuration
|
|
|
|
*/
|
|
|
|
async function getNpmProxyConfig(): Promise<Pick<FetchOptions, "proxy" | "noProxy">> {
|
2023-03-29 09:54:29 +02:00
|
|
|
const cfg = await getNmpConfig();
|
2023-03-07 16:43:12 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
const proxy = cfg["https-proxy"] ?? cfg["proxy"];
|
|
|
|
const noProxy = cfg["noproxy"] ?? cfg["no-proxy"];
|
2023-03-07 16:43:12 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
return { proxy, noProxy };
|
2023-01-16 14:42:20 +01:00
|
|
|
}
|
2022-08-20 14:56:20 +07:00
|
|
|
|
2023-03-31 13:25:48 +02:00
|
|
|
export async function downloadAndUnzip(params: { url: string; destDirPath: string; pathOfDirToExtractInArchive?: string }) {
|
|
|
|
const { url, destDirPath, pathOfDirToExtractInArchive } = params;
|
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
const downloadHash = hash(JSON.stringify({ url })).substring(0, 15);
|
|
|
|
const projectRoot = getProjectRoot();
|
2023-03-31 13:25:48 +02:00
|
|
|
const cacheRoot = process.env.XDG_CACHE_HOME ?? pathJoin(projectRoot, "node_modules", ".cache");
|
2023-03-29 09:54:29 +02:00
|
|
|
const zipFilePath = pathJoin(cacheRoot, "keycloakify", "zip", `_${downloadHash}.zip`);
|
|
|
|
const extractDirPath = pathJoin(cacheRoot, "keycloakify", "unzip", `_${downloadHash}`);
|
|
|
|
|
|
|
|
if (!(await exists(zipFilePath))) {
|
|
|
|
const proxyOpts = await getNpmProxyConfig();
|
|
|
|
const response = await fetch(url, proxyOpts);
|
2023-03-31 13:25:48 +02:00
|
|
|
await mkdir(pathDirname(zipFilePath), { "recursive": true });
|
2023-03-29 09:54:29 +02:00
|
|
|
/**
|
|
|
|
* The correct way to fix this is to upgrade node-fetch beyond 3.2.5
|
|
|
|
* (see https://github.com/node-fetch/node-fetch/issues/1295#issuecomment-1144061991.)
|
|
|
|
* Unfortunately, octokit (a dependency of keycloakify) also uses node-fetch, and
|
|
|
|
* does not support node-fetch 3.x. So we stick around with this band-aid until
|
|
|
|
* octokit upgrades.
|
|
|
|
*/
|
|
|
|
response.body?.setMaxListeners(Number.MAX_VALUE);
|
|
|
|
assert(typeof response.body !== "undefined" && response.body != null);
|
|
|
|
await writeFile(zipFilePath, response.body);
|
|
|
|
}
|
2021-03-03 02:31:02 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
await unzip(zipFilePath, extractDirPath, pathOfDirToExtractInArchive);
|
2023-01-16 14:42:20 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
transformCodebase({
|
|
|
|
"srcDirPath": extractDirPath,
|
|
|
|
"destDirPath": destDirPath
|
|
|
|
});
|
2021-10-11 21:35:40 +02:00
|
|
|
}
|