keycloak start command options support in config
This commit is contained in:
parent
66623e3324
commit
02f2124126
@ -314,7 +314,7 @@ export async function generateResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||||
url: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
urlOrPath: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
||||||
cacheDirPath: buildContext.cacheDirPath,
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
fetchOptions: buildContext.fetchOptions,
|
fetchOptions: buildContext.fetchOptions,
|
||||||
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
||||||
|
@ -61,6 +61,18 @@ export type BuildContext = {
|
|||||||
keycloakVersionRange: KeycloakVersionRange;
|
keycloakVersionRange: KeycloakVersionRange;
|
||||||
jarFileBasename: string;
|
jarFileBasename: string;
|
||||||
}[];
|
}[];
|
||||||
|
startKeycloakOptions: {
|
||||||
|
dockerImage:
|
||||||
|
| {
|
||||||
|
reference: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
dockerExtraArgs: string[];
|
||||||
|
keycloakExtraArgs: string[];
|
||||||
|
extensionJars: ({ type: "path"; path: string } | { type: "url"; url: string })[];
|
||||||
|
realmJsonFilePath: string | undefined;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<Equals<keyof BuildContext["implementedThemeTypes"], ThemeType | "email">>();
|
assert<Equals<keyof BuildContext["implementedThemeTypes"], ThemeType | "email">>();
|
||||||
@ -75,6 +87,13 @@ export type BuildOptions = {
|
|||||||
loginThemeResourcesFromKeycloakVersion?: string;
|
loginThemeResourcesFromKeycloakVersion?: string;
|
||||||
keycloakifyBuildDirPath?: string;
|
keycloakifyBuildDirPath?: string;
|
||||||
kcContextExclusionsFtl?: string;
|
kcContextExclusionsFtl?: string;
|
||||||
|
startKeycloakOptions?: {
|
||||||
|
dockerImage?: string;
|
||||||
|
dockerExtraArgs?: string[];
|
||||||
|
keycloakExtraArgs?: string[];
|
||||||
|
extensionJars?: string[];
|
||||||
|
realmJsonFilePath?: string;
|
||||||
|
};
|
||||||
} & BuildOptions.AccountThemeImplAndKeycloakVersionTargets;
|
} & BuildOptions.AccountThemeImplAndKeycloakVersionTargets;
|
||||||
|
|
||||||
export namespace BuildOptions {
|
export namespace BuildOptions {
|
||||||
@ -301,6 +320,22 @@ export function getBuildContext(params: {
|
|||||||
return id<z.ZodType<TargetType>>(zTargetType);
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const zStartKeycloakOptions = (() => {
|
||||||
|
type TargetType = NonNullable<BuildOptions["startKeycloakOptions"]>;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
dockerImage: z.string().optional(),
|
||||||
|
extensionJars: z.array(z.string()).optional(),
|
||||||
|
realmJsonFilePath: z.string().optional(),
|
||||||
|
dockerExtraArgs: z.array(z.string()).optional(),
|
||||||
|
keycloakExtraArgs: z.array(z.string()).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
const zBuildOptions = (() => {
|
const zBuildOptions = (() => {
|
||||||
type TargetType = BuildOptions;
|
type TargetType = BuildOptions;
|
||||||
|
|
||||||
@ -321,7 +356,8 @@ export function getBuildContext(params: {
|
|||||||
groupId: z.string().optional(),
|
groupId: z.string().optional(),
|
||||||
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
||||||
keycloakifyBuildDirPath: z.string().optional(),
|
keycloakifyBuildDirPath: z.string().optional(),
|
||||||
kcContextExclusionsFtl: z.string().optional()
|
kcContextExclusionsFtl: z.string().optional(),
|
||||||
|
startKeycloakOptions: zStartKeycloakOptions.optional()
|
||||||
}),
|
}),
|
||||||
zAccountThemeImplAndKeycloakVersionTargets
|
zAccountThemeImplAndKeycloakVersionTargets
|
||||||
);
|
);
|
||||||
@ -891,6 +927,47 @@ export function getBuildContext(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return jarTargets;
|
return jarTargets;
|
||||||
})()
|
})(),
|
||||||
|
startKeycloakOptions: {
|
||||||
|
dockerImage: (() => {
|
||||||
|
if (buildOptions.startKeycloakOptions?.dockerImage === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [reference, tag, ...rest] =
|
||||||
|
buildOptions.startKeycloakOptions.dockerImage.split(":");
|
||||||
|
|
||||||
|
assert(
|
||||||
|
reference !== undefined && tag !== undefined && rest.length === 0,
|
||||||
|
`Invalid docker image: ${buildOptions.startKeycloakOptions.dockerImage}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return { reference, tag };
|
||||||
|
})(),
|
||||||
|
dockerExtraArgs: buildOptions.startKeycloakOptions?.dockerExtraArgs ?? [],
|
||||||
|
keycloakExtraArgs: buildOptions.startKeycloakOptions?.keycloakExtraArgs ?? [],
|
||||||
|
extensionJars: (buildOptions.startKeycloakOptions?.extensionJars ?? []).map(
|
||||||
|
urlOrPath => {
|
||||||
|
if (/^https?:\/\//.test(urlOrPath)) {
|
||||||
|
return { type: "url", url: urlOrPath };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "path",
|
||||||
|
path: getAbsoluteAndInOsFormatPath({
|
||||||
|
pathIsh: urlOrPath,
|
||||||
|
cwd: projectDirPath
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
),
|
||||||
|
realmJsonFilePath:
|
||||||
|
buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined
|
||||||
|
? undefined
|
||||||
|
: getAbsoluteAndInOsFormatPath({
|
||||||
|
pathIsh: buildOptions.startKeycloakOptions.realmJsonFilePath,
|
||||||
|
cwd: projectDirPath
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export async function downloadKeycloakDefaultTheme(params: {
|
|||||||
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
|
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
|
||||||
|
|
||||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||||
url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
urlOrPath: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
||||||
cacheDirPath: buildContext.cacheDirPath,
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
fetchOptions: buildContext.fetchOptions,
|
fetchOptions: buildContext.fetchOptions,
|
||||||
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",
|
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",
|
||||||
|
@ -40,7 +40,7 @@ async function appBuild_vite(params: {
|
|||||||
|
|
||||||
const dIsSuccess = new Deferred<boolean>();
|
const dIsSuccess = new Deferred<boolean>();
|
||||||
|
|
||||||
console.log(chalk.blue("Running: 'npx vite build'"));
|
console.log(chalk.blue("$ npx vite build"));
|
||||||
|
|
||||||
const child = child_process.spawn("npx", ["vite", "build"], {
|
const child = child_process.spawn("npx", ["vite", "build"], {
|
||||||
cwd: buildContext.projectDirPath,
|
cwd: buildContext.projectDirPath,
|
||||||
@ -145,7 +145,7 @@ async function appBuild_webpack(params: {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chalk.blue(`Running: '${subCommand}'`));
|
console.log(chalk.blue(`$ ${subCommand}`));
|
||||||
|
|
||||||
const child = child_process.spawn(command, args, {
|
const child = child_process.spawn(command, args, {
|
||||||
cwd: commandCwd,
|
cwd: commandCwd,
|
||||||
|
@ -20,7 +20,7 @@ export async function keycloakifyBuild(params: {
|
|||||||
|
|
||||||
const dResult = new Deferred<{ isSuccess: boolean }>();
|
const dResult = new Deferred<{ isSuccess: boolean }>();
|
||||||
|
|
||||||
console.log(chalk.blue("Running: 'npx keycloakify build'"));
|
console.log(chalk.blue("$ npx keycloakify build"));
|
||||||
|
|
||||||
const child = child_process.spawn("npx", ["keycloakify", "build"], {
|
const child = child_process.spawn("npx", ["keycloakify", "build"], {
|
||||||
cwd: buildContext.projectDirPath,
|
cwd: buildContext.projectDirPath,
|
||||||
|
@ -4,7 +4,7 @@ import type { CliCommandOptions as CliCommandOptions_common } from "../main";
|
|||||||
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
||||||
import { ACCOUNT_V1_THEME_NAME, CONTAINER_NAME } from "../shared/constants";
|
import { ACCOUNT_V1_THEME_NAME, CONTAINER_NAME } from "../shared/constants";
|
||||||
import { SemVer } from "../tools/SemVer";
|
import { SemVer } from "../tools/SemVer";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert, type Equals } from "tsafe/assert";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {
|
import {
|
||||||
join as pathJoin,
|
join as pathJoin,
|
||||||
@ -26,6 +26,7 @@ import { keycloakifyBuild } from "./keycloakifyBuild";
|
|||||||
import { isInside } from "../tools/isInside";
|
import { isInside } from "../tools/isInside";
|
||||||
import { existsAsync } from "../tools/fs.existsAsync";
|
import { existsAsync } from "../tools/fs.existsAsync";
|
||||||
import { rm } from "../tools/fs.rm";
|
import { rm } from "../tools/fs.rm";
|
||||||
|
import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive";
|
||||||
|
|
||||||
export type CliCommandOptions = CliCommandOptions_common & {
|
export type CliCommandOptions = CliCommandOptions_common & {
|
||||||
port: number;
|
port: number;
|
||||||
@ -88,11 +89,14 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
const buildContext = getBuildContext({ cliCommandOptions });
|
const buildContext = getBuildContext({ cliCommandOptions });
|
||||||
|
|
||||||
const { keycloakVersion } = await (async () => {
|
const { dockerImageTag } = await (async () => {
|
||||||
if (cliCommandOptions.keycloakVersion !== undefined) {
|
if (cliCommandOptions.keycloakVersion !== undefined) {
|
||||||
|
return { dockerImageTag: cliCommandOptions.keycloakVersion };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildContext.startKeycloakOptions.dockerImage !== undefined) {
|
||||||
return {
|
return {
|
||||||
keycloakVersion: cliCommandOptions.keycloakVersion,
|
dockerImageTag: buildContext.startKeycloakOptions.dockerImage.tag
|
||||||
keycloakMajorNumber: SemVer.parse(cliCommandOptions.keycloakVersion).major
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,10 +119,35 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
console.log(`→ ${keycloakVersion}`);
|
console.log(`→ ${keycloakVersion}`);
|
||||||
|
|
||||||
return { keycloakVersion };
|
return { dockerImageTag: keycloakVersion };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const keycloakMajorVersionNumber = SemVer.parse(keycloakVersion).major;
|
const keycloakMajorVersionNumber = (() => {
|
||||||
|
if (buildContext.startKeycloakOptions.dockerImage === undefined) {
|
||||||
|
return SemVer.parse(dockerImageTag).major;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tag } = buildContext.startKeycloakOptions.dockerImage;
|
||||||
|
|
||||||
|
const [wrap] = [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
|
||||||
|
.map(majorVersionNumber => ({
|
||||||
|
majorVersionNumber,
|
||||||
|
index: tag.indexOf(`${majorVersionNumber}`)
|
||||||
|
}))
|
||||||
|
.filter(({ index }) => index !== -1)
|
||||||
|
.sort((a, b) => a.index - b.index);
|
||||||
|
|
||||||
|
if (wrap === undefined) {
|
||||||
|
console.warn(
|
||||||
|
chalk.yellow(
|
||||||
|
`Could not determine the major Keycloak version number from the docker image tag ${tag}. Assuming 25`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrap.majorVersionNumber;
|
||||||
|
})();
|
||||||
|
|
||||||
{
|
{
|
||||||
const { isAppBuildSuccess } = await appBuild({
|
const { isAppBuildSuccess } = await appBuild({
|
||||||
@ -157,26 +186,50 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
assert(jarFilePath !== undefined);
|
assert(jarFilePath !== undefined);
|
||||||
|
|
||||||
console.log(`Using ${chalk.bold(pathBasename(jarFilePath))}`);
|
const extensionJarFilePaths = await Promise.all(
|
||||||
|
buildContext.startKeycloakOptions.extensionJars.map(async extensionJar => {
|
||||||
|
switch (extensionJar.type) {
|
||||||
|
case "path": {
|
||||||
|
assert(
|
||||||
|
await existsAsync(extensionJar.path),
|
||||||
|
`${extensionJar.path} does not exist`
|
||||||
|
);
|
||||||
|
return extensionJar.path;
|
||||||
|
}
|
||||||
|
case "url": {
|
||||||
|
const { archiveFilePath } = await downloadAndExtractArchive({
|
||||||
|
cacheDirPath: buildContext.cacheDirPath,
|
||||||
|
fetchOptions: buildContext.fetchOptions,
|
||||||
|
urlOrPath: extensionJar.url,
|
||||||
|
uniqueIdOfOnArchiveFile: "no extraction",
|
||||||
|
onArchiveFile: async () => {}
|
||||||
|
});
|
||||||
|
return archiveFilePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert<Equals<typeof extensionJar, never>>(false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const realmJsonFilePath = await (async () => {
|
const realmJsonFilePath = await (async () => {
|
||||||
if (cliCommandOptions.realmJsonFilePath !== undefined) {
|
if (cliCommandOptions.realmJsonFilePath !== undefined) {
|
||||||
if (cliCommandOptions.realmJsonFilePath === "none") {
|
if (cliCommandOptions.realmJsonFilePath === "none") {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
|
||||||
chalk.green(
|
|
||||||
`Using realm json file: ${cliCommandOptions.realmJsonFilePath}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return getAbsoluteAndInOsFormatPath({
|
return getAbsoluteAndInOsFormatPath({
|
||||||
pathIsh: cliCommandOptions.realmJsonFilePath,
|
pathIsh: cliCommandOptions.realmJsonFilePath,
|
||||||
cwd: process.cwd()
|
cwd: process.cwd()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buildContext.startKeycloakOptions.realmJsonFilePath !== undefined) {
|
||||||
|
assert(
|
||||||
|
await existsAsync(buildContext.startKeycloakOptions.realmJsonFilePath),
|
||||||
|
`${pathRelative(process.cwd(), buildContext.startKeycloakOptions.realmJsonFilePath)} does not exist`
|
||||||
|
);
|
||||||
|
return buildContext.startKeycloakOptions.realmJsonFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
const internalFilePath = await (async () => {
|
const internalFilePath = await (async () => {
|
||||||
const dirPath = pathJoin(
|
const dirPath = pathJoin(
|
||||||
getThisCodebaseRootDirPath(),
|
getThisCodebaseRootDirPath(),
|
||||||
@ -281,26 +334,23 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
});
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
const spawnArgs = [
|
const dockerRunArgs: string[] = [
|
||||||
"docker",
|
`-p ${cliCommandOptions.port}:8080`,
|
||||||
[
|
`--name ${CONTAINER_NAME}`,
|
||||||
"run",
|
`-e KEYCLOAK_ADMIN=admin`,
|
||||||
...["-p", `${cliCommandOptions.port}:8080`],
|
`-e KEYCLOAK_ADMIN_PASSWORD=admin`,
|
||||||
...["--name", CONTAINER_NAME],
|
|
||||||
...["-e", "KEYCLOAK_ADMIN=admin"],
|
|
||||||
...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
|
|
||||||
...(realmJsonFilePath === undefined
|
...(realmJsonFilePath === undefined
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
"-v",
|
`-v ".${pathSep}${pathRelative(process.cwd(), realmJsonFilePath)}":/opt/keycloak/data/import/myrealm-realm.json`
|
||||||
`"${realmJsonFilePath}":/opt/keycloak/data/import/myrealm-realm.json`
|
|
||||||
]),
|
]),
|
||||||
...[
|
`-v "./${pathRelative(process.cwd(), jarFilePath_cacheDir)}":/opt/keycloak/providers/keycloak-theme.jar`,
|
||||||
"-v",
|
...extensionJarFilePaths.map(
|
||||||
`"${jarFilePath_cacheDir}":/opt/keycloak/providers/keycloak-theme.jar`
|
jarFilePath =>
|
||||||
],
|
`-v ".${pathSep}${pathRelative(process.cwd(), jarFilePath)}":/opt/keycloak/providers/${pathBasename(jarFilePath)}`
|
||||||
|
),
|
||||||
...(keycloakMajorVersionNumber <= 20
|
...(keycloakMajorVersionNumber <= 20
|
||||||
? ["-e", "JAVA_OPTS=-Dkeycloak.profile=preview"]
|
? ["-e JAVA_OPTS=-Dkeycloak.profile=preview"]
|
||||||
: []),
|
: []),
|
||||||
...[
|
...[
|
||||||
...buildContext.themeNames,
|
...buildContext.themeNames,
|
||||||
@ -322,36 +372,46 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
),
|
),
|
||||||
containerDirPath: `/opt/keycloak/themes/${themeName}`
|
containerDirPath: `/opt/keycloak/themes/${themeName}`
|
||||||
}))
|
}))
|
||||||
.map(({ localDirPath, containerDirPath }) => [
|
.map(
|
||||||
"-v",
|
({ localDirPath, containerDirPath }) =>
|
||||||
`"${localDirPath}":${containerDirPath}:rw`
|
`-v ".${pathSep}${pathRelative(process.cwd(), localDirPath)}":${containerDirPath}:rw`
|
||||||
])
|
),
|
||||||
.flat(),
|
|
||||||
...buildContext.environmentVariables
|
...buildContext.environmentVariables
|
||||||
.map(({ name }) => ({ name, envValue: process.env[name] }))
|
.map(({ name }) => ({ name, envValue: process.env[name] }))
|
||||||
.map(({ name, envValue }) =>
|
.map(({ name, envValue }) =>
|
||||||
envValue === undefined ? undefined : { name, envValue }
|
envValue === undefined ? undefined : { name, envValue }
|
||||||
)
|
)
|
||||||
.filter(exclude(undefined))
|
.filter(exclude(undefined))
|
||||||
.map(({ name, envValue }) => [
|
.map(
|
||||||
"--env",
|
({ name, envValue }) =>
|
||||||
`${name}='${envValue.replace(/'/g, "'\\''")}'`
|
`--env ${name}='${envValue.replace(/'/g, "'\\''")}'`
|
||||||
])
|
),
|
||||||
.flat(),
|
...buildContext.startKeycloakOptions.dockerExtraArgs,
|
||||||
`quay.io/keycloak/keycloak:${keycloakVersion}`,
|
`${buildContext.startKeycloakOptions.dockerImage?.reference ?? "quay.io/keycloak/keycloak"}:${dockerImageTag}`,
|
||||||
"start-dev",
|
"start-dev",
|
||||||
...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24
|
...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24
|
||||||
? ["--features=declarative-user-profile"]
|
? ["--features=declarative-user-profile"]
|
||||||
: []),
|
: []),
|
||||||
...(realmJsonFilePath === undefined ? [] : ["--import-realm"])
|
...(realmJsonFilePath === undefined ? [] : ["--import-realm"]),
|
||||||
],
|
...buildContext.startKeycloakOptions.keycloakExtraArgs
|
||||||
{
|
];
|
||||||
cwd: buildContext.keycloakifyBuildDirPath,
|
|
||||||
shell: true
|
|
||||||
}
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const child = child_process.spawn(...spawnArgs);
|
console.log(
|
||||||
|
chalk.blue(
|
||||||
|
[
|
||||||
|
`$ docker run \\`,
|
||||||
|
...dockerRunArgs.map(
|
||||||
|
(line, i, arr) => ` ${line}${arr.length - 1 === i ? "" : " \\"}`
|
||||||
|
)
|
||||||
|
].join("\n")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const child = child_process.spawn(
|
||||||
|
"docker",
|
||||||
|
["run", ...dockerRunArgs.map(line => line.split(" ")).flat()],
|
||||||
|
{ shell: true }
|
||||||
|
);
|
||||||
|
|
||||||
child.stdout.on("data", data => process.stdout.write(data));
|
child.stdout.on("data", data => process.stdout.write(data));
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import fetch, { type FetchOptions } from "make-fetch-happen";
|
import fetch, { type FetchOptions } from "make-fetch-happen";
|
||||||
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
|
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
|
||||||
import { dirname as pathDirname, join as pathJoin } from "path";
|
import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { extractArchive } from "./extractArchive";
|
import { extractArchive } from "./extractArchive";
|
||||||
import { existsAsync } from "./fs.existsAsync";
|
import { existsAsync } from "./fs.existsAsync";
|
||||||
|
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import { rm } from "./fs.rm";
|
import { rm } from "./fs.rm";
|
||||||
|
import * as fsPr from "fs/promises";
|
||||||
|
|
||||||
export async function downloadAndExtractArchive(params: {
|
export async function downloadAndExtractArchive(params: {
|
||||||
url: string;
|
urlOrPath: string;
|
||||||
uniqueIdOfOnArchiveFile: string;
|
uniqueIdOfOnArchiveFile: string;
|
||||||
onArchiveFile: (params: {
|
onArchiveFile: (params: {
|
||||||
fileRelativePath: string;
|
fileRelativePath: string;
|
||||||
@ -21,15 +21,34 @@ export async function downloadAndExtractArchive(params: {
|
|||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
cacheDirPath: string;
|
cacheDirPath: string;
|
||||||
fetchOptions: FetchOptions | undefined;
|
fetchOptions: FetchOptions | undefined;
|
||||||
}): Promise<{ extractedDirPath: string }> {
|
}): Promise<{ extractedDirPath: string; archiveFilePath: string; }> {
|
||||||
const { url, uniqueIdOfOnArchiveFile, onArchiveFile, cacheDirPath, fetchOptions } =
|
const {
|
||||||
params;
|
urlOrPath,
|
||||||
|
uniqueIdOfOnArchiveFile,
|
||||||
|
onArchiveFile,
|
||||||
|
cacheDirPath,
|
||||||
|
fetchOptions
|
||||||
|
} = params;
|
||||||
|
|
||||||
const archiveFileBasename = url.split("?")[0].split("/").reverse()[0];
|
const isUrl = /^https?:\/\//.test(urlOrPath);
|
||||||
|
|
||||||
|
const archiveFileBasename = isUrl
|
||||||
|
? urlOrPath.split("?")[0].split("/").reverse()[0]
|
||||||
|
: pathBasename(urlOrPath);
|
||||||
|
|
||||||
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
|
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
|
||||||
|
|
||||||
download: {
|
download: {
|
||||||
|
await mkdir(pathDirname(archiveFilePath), { recursive: true });
|
||||||
|
|
||||||
|
if (!isUrl) {
|
||||||
|
await fsPr.copyFile(urlOrPath, archiveFilePath);
|
||||||
|
|
||||||
|
break download;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = urlOrPath;
|
||||||
|
|
||||||
if (await existsAsync(archiveFilePath)) {
|
if (await existsAsync(archiveFilePath)) {
|
||||||
const isDownloaded = await SuccessTracker.getIsDownloaded({
|
const isDownloaded = await SuccessTracker.getIsDownloaded({
|
||||||
cacheDirPath,
|
cacheDirPath,
|
||||||
@ -48,8 +67,6 @@ export async function downloadAndExtractArchive(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await mkdir(pathDirname(archiveFilePath), { recursive: true });
|
|
||||||
|
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await fetch(url, fetchOptions);
|
||||||
|
|
||||||
response.body?.setMaxListeners(Number.MAX_VALUE);
|
response.body?.setMaxListeners(Number.MAX_VALUE);
|
||||||
@ -136,7 +153,7 @@ export async function downloadAndExtractArchive(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { extractedDirPath };
|
return { extractedDirPath, archiveFilePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuccessTracker = {
|
type SuccessTracker = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user