2021-02-21 23:40:10 +01:00
|
|
|
#!/usr/bin/env node
|
2021-03-03 02:31:02 +01:00
|
|
|
import { join as pathJoin } from "path";
|
2021-10-11 21:35:40 +02:00
|
|
|
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
2022-04-20 00:39:40 +02:00
|
|
|
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
2022-09-08 12:06:26 +03:00
|
|
|
import { getLogger } from "./tools/logger";
|
2024-01-30 06:38:26 +01:00
|
|
|
import { readBuildOptions, type BuildOptions } from "./keycloakify/buildOptions";
|
2023-09-03 23:26:34 +02:00
|
|
|
import { assert } from "tsafe/assert";
|
2023-08-20 03:00:45 +02:00
|
|
|
import * as child_process from "child_process";
|
|
|
|
import * as fs from "fs";
|
2024-02-05 08:52:58 +01:00
|
|
|
import { rmSync } from "./tools/fs.rmSync";
|
|
|
|
import { lastKeycloakVersionWithAccountV1 } from "./constants";
|
|
|
|
import { transformCodebase } from "./tools/transformCodebase";
|
2021-02-21 23:06:42 +01:00
|
|
|
|
2023-09-03 23:26:34 +02:00
|
|
|
export type BuildOptionsLike = {
|
|
|
|
cacheDirPath: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
|
|
|
|
|
|
|
export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; buildOptions: BuildOptionsLike }) {
|
|
|
|
const { keycloakVersion, destDirPath, buildOptions } = params;
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
await downloadAndUnzip({
|
2023-08-21 05:54:17 +02:00
|
|
|
"doUseCache": true,
|
2023-09-03 23:26:34 +02:00
|
|
|
"cacheDirPath": buildOptions.cacheDirPath,
|
2023-08-21 04:26:58 +02:00
|
|
|
destDirPath,
|
|
|
|
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
|
|
|
"specificDirsToExtract": ["", "-community"].map(ext => `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`),
|
|
|
|
"preCacheTransform": {
|
2023-08-24 08:58:00 +02:00
|
|
|
"actionCacheId": "npm install and build",
|
2023-08-21 04:26:58 +02:00
|
|
|
"action": async ({ destDirPath }) => {
|
|
|
|
install_common_node_modules: {
|
|
|
|
const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
if (!fs.existsSync(commonResourcesDirPath)) {
|
|
|
|
break install_common_node_modules;
|
|
|
|
}
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
if (!fs.existsSync(pathJoin(commonResourcesDirPath, "package.json"))) {
|
|
|
|
break install_common_node_modules;
|
|
|
|
}
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
if (fs.existsSync(pathJoin(commonResourcesDirPath, "node_modules"))) {
|
|
|
|
break install_common_node_modules;
|
|
|
|
}
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
child_process.execSync("npm install --omit=dev", {
|
|
|
|
"cwd": commonResourcesDirPath,
|
|
|
|
"stdio": "ignore"
|
|
|
|
});
|
|
|
|
}
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
install_and_move_to_common_resources_generated_in_keycloak_v2: {
|
|
|
|
const accountV2DirSrcDirPath = pathJoin(destDirPath, "keycloak.v2", "account", "src");
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
if (!fs.existsSync(accountV2DirSrcDirPath)) {
|
|
|
|
break install_and_move_to_common_resources_generated_in_keycloak_v2;
|
|
|
|
}
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2024-01-18 00:54:09 +01:00
|
|
|
const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
|
|
|
2024-01-18 01:02:14 +01:00
|
|
|
if (packageManager === "pnpm") {
|
|
|
|
try {
|
|
|
|
child_process.execSync(`which pnpm`);
|
|
|
|
} catch {
|
|
|
|
console.log(`Installing pnpm globally`);
|
|
|
|
child_process.execSync(`npm install -g pnpm`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-18 00:54:09 +01:00
|
|
|
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8"));
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
parsedPackageJson.scripts.build = parsedPackageJson.scripts.build
|
2024-01-18 00:54:09 +01:00
|
|
|
.replace(`${packageManager} run check-types`, "true")
|
|
|
|
.replace(`${packageManager} run babel`, "true");
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2024-01-18 00:54:09 +01:00
|
|
|
child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
|
2023-08-20 03:00:45 +02:00
|
|
|
|
2024-02-05 08:52:58 +01:00
|
|
|
rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), { "recursive": true });
|
|
|
|
}
|
|
|
|
|
|
|
|
last_account_v1_transformations: {
|
|
|
|
if (lastKeycloakVersionWithAccountV1 !== keycloakVersion) {
|
|
|
|
break last_account_v1_transformations;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css");
|
|
|
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
accountCssFilePath,
|
|
|
|
Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const totpFtlFilePath = pathJoin(destDirPath, "base", "account", "totp.ftl");
|
|
|
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
totpFtlFilePath,
|
|
|
|
Buffer.from(
|
|
|
|
fs
|
|
|
|
.readFileSync(totpFtlFilePath)
|
|
|
|
.toString("utf8")
|
|
|
|
.replace(
|
|
|
|
[
|
|
|
|
" <#list totp.policy.supportedApplications as app>",
|
|
|
|
" <li>${app}</li>",
|
|
|
|
" </#list>"
|
|
|
|
].join("\n"),
|
|
|
|
[
|
|
|
|
" <#if totp.policy.supportedApplications?has_content>",
|
|
|
|
" <#list totp.policy.supportedApplications as app>",
|
|
|
|
" <li>${app}</li>",
|
|
|
|
" </#list>",
|
|
|
|
" </#if>"
|
|
|
|
].join("\n")
|
|
|
|
),
|
|
|
|
"utf8"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note, this is an optimization for reducing the size of the jar
|
|
|
|
{
|
|
|
|
const defaultThemeCommonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
|
|
|
|
|
|
|
|
const usedCommonResourceRelativeFilePaths = [
|
|
|
|
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(fileBasename =>
|
|
|
|
pathJoin("node_modules", "patternfly", "dist", "css", fileBasename)
|
|
|
|
),
|
|
|
|
...[
|
|
|
|
"OpenSans-Light-webfont.woff2",
|
|
|
|
"OpenSans-Regular-webfont.woff2",
|
|
|
|
"OpenSans-Bold-webfont.woff2",
|
|
|
|
"OpenSans-Semibold-webfont.woff2",
|
|
|
|
"OpenSans-Bold-webfont.woff",
|
|
|
|
"OpenSans-Light-webfont.woff",
|
|
|
|
"OpenSans-Regular-webfont.woff",
|
|
|
|
"OpenSans-Semibold-webfont.woff",
|
|
|
|
"OpenSans-Regular-webfont.ttf",
|
|
|
|
"OpenSans-Light-webfont.ttf",
|
|
|
|
"OpenSans-Semibold-webfont.ttf",
|
|
|
|
"OpenSans-Bold-webfont.ttf"
|
|
|
|
].map(fileBasename => pathJoin("node_modules", "patternfly", "dist", "fonts", fileBasename))
|
|
|
|
];
|
|
|
|
|
|
|
|
transformCodebase({
|
|
|
|
"srcDirPath": defaultThemeCommonResourcesDirPath,
|
|
|
|
"destDirPath": defaultThemeCommonResourcesDirPath,
|
|
|
|
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
|
|
|
|
if (!usedCommonResourceRelativeFilePaths.includes(fileRelativePath)) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { "modifiedSourceCode": sourceCode };
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Other optimization: Remove AngularJS
|
|
|
|
{
|
|
|
|
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
|
|
|
|
|
|
|
|
fs.readdirSync(nodeModuleDirPath)
|
|
|
|
.filter(basename => basename.startsWith("angular"))
|
|
|
|
.map(basename => pathJoin(nodeModuleDirPath, basename))
|
|
|
|
.filter(dirPath => fs.statSync(dirPath).isDirectory())
|
|
|
|
.forEach(dirPath => rmSync(dirPath, { "recursive": true }));
|
|
|
|
}
|
2023-08-21 04:26:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-10-06 17:22:52 +02:00
|
|
|
}
|
2021-02-28 18:40:57 +01:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
async function main() {
|
2023-04-19 05:04:11 +02:00
|
|
|
const buildOptions = readBuildOptions({
|
2023-09-03 23:26:34 +02:00
|
|
|
"reactAppRootDirPath": process.cwd(),
|
2023-04-19 05:04:11 +02:00
|
|
|
"processArgv": process.argv.slice(2)
|
|
|
|
});
|
|
|
|
|
|
|
|
const logger = getLogger({ "isSilent": buildOptions.isSilent });
|
2023-03-29 09:54:29 +02:00
|
|
|
const { keycloakVersion } = await promptKeycloakVersion();
|
|
|
|
|
2023-04-19 05:04:11 +02:00
|
|
|
const destDirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme");
|
2021-10-07 21:00:53 +02:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
2021-10-07 17:32:38 +02:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
await downloadBuiltinKeycloakTheme({
|
|
|
|
keycloakVersion,
|
2023-09-03 23:26:34 +02:00
|
|
|
destDirPath,
|
|
|
|
buildOptions
|
2023-03-29 09:54:29 +02:00
|
|
|
});
|
|
|
|
}
|
2021-10-07 21:00:53 +02:00
|
|
|
|
2023-03-29 09:54:29 +02:00
|
|
|
if (require.main === module) {
|
2023-03-30 22:09:27 +00:00
|
|
|
main();
|
2021-02-28 18:40:57 +01:00
|
|
|
}
|