Build rework checkpoint

This commit is contained in:
Joseph Garrone
2024-06-10 07:57:12 +02:00
parent 9bbc7cc651
commit 84c774503d
13 changed files with 159 additions and 303 deletions

View File

@ -1,63 +0,0 @@
import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
import { promptKeycloakVersion } from "./shared/promptKeycloakVersion";
import { getBuildContext } from "./shared/buildContext";
import { downloadKeycloakDefaultTheme } from "./shared/downloadKeycloakDefaultTheme";
import { transformCodebase } from "./tools/transformCodebase";
import type { CliCommandOptions } from "./main";
import chalk from "chalk";
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
const { cliCommandOptions } = params;
const buildContext = getBuildContext({
cliCommandOptions
});
console.log(
chalk.cyan(
"Select the Keycloak version from which you want to download the builtins theme:"
)
);
const { keycloakVersion } = await promptKeycloakVersion({
startingFromMajor: undefined,
cacheDirPath: buildContext.cacheDirPath
});
console.log(`${keycloakVersion}`);
const destDirPath = pathJoin(
buildContext.keycloakifyBuildDirPath,
"src",
"main",
"resources",
"theme"
);
console.log(
[
`Downloading builtins theme of Keycloak ${keycloakVersion} here:`,
`- ${chalk.bold(
`.${pathSep}${pathJoin(pathRelative(process.cwd(), destDirPath), "base")}`
)}`,
`- ${chalk.bold(
`.${pathSep}${pathJoin(
pathRelative(process.cwd(), destDirPath),
"keycloak"
)}`
)}`
].join("\n")
);
const { defaultThemeDirPath } = await downloadKeycloakDefaultTheme({
keycloakVersion,
buildContext
});
transformCodebase({
srcDirPath: defaultThemeDirPath,
destDirPath
});
console.log(chalk.green(`✓ done`));
}

View File

@ -32,12 +32,14 @@ export async function buildJar(params: {
jarFileBasename: string; jarFileBasename: string;
keycloakAccountV1Version: KeycloakAccountV1Version; keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion; keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
resourcesDirPath: string;
buildContext: BuildContextLike; buildContext: BuildContextLike;
}): Promise<void> { }): Promise<void> {
const { const {
jarFileBasename, jarFileBasename,
keycloakAccountV1Version, keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion, keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
buildContext buildContext
} = params; } = params;
@ -57,7 +59,7 @@ export async function buildJar(params: {
if ( if (
fileRelativePath === fileRelativePath ===
getMetaInfKeycloakThemesJsonFilePath({ keycloakifyBuildDirPath: "." }) getMetaInfKeycloakThemesJsonFilePath({ resourcesDirPath: "." })
) { ) {
return { modifiedSourceCode: sourceCode }; return { modifiedSourceCode: sourceCode };
} }
@ -65,7 +67,7 @@ export async function buildJar(params: {
for (const themeName of [...buildContext.themeNames, accountV1ThemeName]) { for (const themeName of [...buildContext.themeNames, accountV1ThemeName]) {
if ( if (
isInside({ isInside({
dirPath: pathJoin("src", "main", "resources", "theme", themeName), dirPath: pathJoin("theme", themeName),
filePath: fileRelativePath filePath: fileRelativePath
}) })
) { ) {
@ -87,13 +89,7 @@ export async function buildJar(params: {
if ( if (
isInside({ isInside({
dirPath: pathJoin( dirPath: pathJoin("theme", accountV1ThemeName),
"src",
"main",
"resources",
"theme",
accountV1ThemeName
),
filePath: fileRelativePath filePath: fileRelativePath
}) })
) { ) {
@ -103,7 +99,7 @@ export async function buildJar(params: {
if ( if (
fileRelativePath === fileRelativePath ===
getMetaInfKeycloakThemesJsonFilePath({ getMetaInfKeycloakThemesJsonFilePath({
keycloakifyBuildDirPath: "." resourcesDirPath: "."
}) })
) { ) {
const keycloakThemesJsonParsed = JSON.parse( const keycloakThemesJsonParsed = JSON.parse(
@ -128,15 +124,7 @@ export async function buildJar(params: {
for (const themeName of buildContext.themeNames) { for (const themeName of buildContext.themeNames) {
if ( if (
fileRelativePath === fileRelativePath ===
pathJoin( pathJoin("theme", themeName, "account", "theme.properties")
"src",
"main",
"resources",
"theme",
themeName,
"account",
"theme.properties"
)
) { ) {
const modifiedSourceCode = Buffer.from( const modifiedSourceCode = Buffer.from(
sourceCode sourceCode
@ -160,8 +148,8 @@ export async function buildJar(params: {
}; };
transformCodebase({ transformCodebase({
srcDirPath: buildContext.keycloakifyBuildDirPath, srcDirPath: resourcesDirPath,
destDirPath: keycloakifyBuildTmpDirPath, destDirPath: pathJoin(keycloakifyBuildTmpDirPath, "src", "main", "resources"),
transformSourceCode: params => { transformSourceCode: params => {
const resultCommon = transformCodebase_common(params); const resultCommon = transformCodebase_common(params);

View File

@ -8,7 +8,7 @@ import { getKeycloakVersionRangeForJar } from "./getKeycloakVersionRangeForJar";
import { buildJar, BuildContextLike as BuildContextLike_buildJar } from "./buildJar"; import { buildJar, BuildContextLike as BuildContextLike_buildJar } from "./buildJar";
import type { BuildContext } from "../../shared/buildContext"; import type { BuildContext } from "../../shared/buildContext";
import { getJarFileBasename } from "../../shared/getJarFileBasename"; import { getJarFileBasename } from "../../shared/getJarFileBasename";
import { readMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes"; import { readMetaInfKeycloakThemes_fromResourcesDirPath } from "../../shared/metaInfKeycloakThemes";
import { accountV1ThemeName } from "../../shared/constants"; import { accountV1ThemeName } from "../../shared/constants";
export type BuildContextLike = BuildContextLike_buildJar & { export type BuildContextLike = BuildContextLike_buildJar & {
@ -18,12 +18,13 @@ export type BuildContextLike = BuildContextLike_buildJar & {
assert<BuildContext extends BuildContextLike ? true : false>(); assert<BuildContext extends BuildContextLike ? true : false>();
export async function buildJars(params: { export async function buildJars(params: {
resourcesDirPath: string;
buildContext: BuildContextLike; buildContext: BuildContextLike;
}): Promise<void> { }): Promise<void> {
const { buildContext } = params; const { resourcesDirPath, buildContext } = params;
const doesImplementAccountTheme = readMetaInfKeycloakThemes({ const doesImplementAccountTheme = readMetaInfKeycloakThemes_fromResourcesDirPath({
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath resourcesDirPath: buildContext.keycloakifyBuildDirPath
}).themes.some(({ name }) => name === accountV1ThemeName); }).themes.some(({ name }) => name === accountV1ThemeName);
await Promise.all( await Promise.all(
@ -71,6 +72,7 @@ export async function buildJars(params: {
jarFileBasename, jarFileBasename,
keycloakAccountV1Version, keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion, keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
buildContext buildContext
}) })
) )

View File

@ -13,13 +13,15 @@ import { transformCodebase } from "../../tools/transformCodebase";
export type BuildContextLike = { export type BuildContextLike = {
cacheDirPath: string; cacheDirPath: string;
npmWorkspaceRootDirPath: string; npmWorkspaceRootDirPath: string;
keycloakifyBuildDirPath: string;
}; };
assert<BuildContext extends BuildContextLike ? true : false>(); assert<BuildContext extends BuildContextLike ? true : false>();
export async function bringInAccountV1(params: { buildContext: BuildContextLike }) { export async function bringInAccountV1(params: {
const { buildContext } = params; resourcesDirPath: string;
buildContext: BuildContextLike;
}) {
const { resourcesDirPath, buildContext } = params;
const { defaultThemeDirPath } = await downloadKeycloakDefaultTheme({ const { defaultThemeDirPath } = await downloadKeycloakDefaultTheme({
keycloakVersion: lastKeycloakVersionWithAccountV1, keycloakVersion: lastKeycloakVersionWithAccountV1,
@ -27,10 +29,7 @@ export async function bringInAccountV1(params: { buildContext: BuildContextLike
}); });
const accountV1DirPath = pathJoin( const accountV1DirPath = pathJoin(
buildContext.keycloakifyBuildDirPath, resourcesDirPath,
"src",
"main",
"resources",
"theme", "theme",
accountV1ThemeName, accountV1ThemeName,
"account" "account"

View File

@ -5,6 +5,8 @@ import {
type BuildContextLike as BuildContextLike_generateSrcMainResourcesForMainTheme type BuildContextLike as BuildContextLike_generateSrcMainResourcesForMainTheme
} from "./generateSrcMainResourcesForMainTheme"; } from "./generateSrcMainResourcesForMainTheme";
import { generateSrcMainResourcesForThemeVariant } from "./generateSrcMainResourcesForThemeVariant"; import { generateSrcMainResourcesForThemeVariant } from "./generateSrcMainResourcesForThemeVariant";
import fs from "fs";
import { rmSync } from "../../tools/fs.rmSync";
export type BuildContextLike = BuildContextLike_generateSrcMainResourcesForMainTheme & { export type BuildContextLike = BuildContextLike_generateSrcMainResourcesForMainTheme & {
themeNames: string[]; themeNames: string[];
@ -14,21 +16,27 @@ assert<BuildContext extends BuildContextLike ? true : false>();
export async function generateSrcMainResources(params: { export async function generateSrcMainResources(params: {
buildContext: BuildContextLike; buildContext: BuildContextLike;
resourcesDirPath: string;
}): Promise<void> { }): Promise<void> {
const { buildContext } = params; const { resourcesDirPath, buildContext } = params;
const [themeName, ...themeVariantNames] = buildContext.themeNames; const [themeName, ...themeVariantNames] = buildContext.themeNames;
if (fs.existsSync(resourcesDirPath)) {
rmSync(resourcesDirPath, { recursive: true });
}
await generateSrcMainResourcesForMainTheme({ await generateSrcMainResourcesForMainTheme({
resourcesDirPath,
themeName, themeName,
buildContext buildContext
}); });
for (const themeVariantName of themeVariantNames) { for (const themeVariantName of themeVariantNames) {
generateSrcMainResourcesForThemeVariant({ generateSrcMainResourcesForThemeVariant({
resourcesDirPath,
themeName, themeName,
themeVariantName, themeVariantName
buildContext
}); });
} }
} }

View File

@ -43,14 +43,10 @@ import { escapeStringForPropertiesFile } from "../../tools/escapeStringForProper
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode & export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
BuildContextLike_downloadKeycloakStaticResources & BuildContextLike_downloadKeycloakStaticResources &
BuildContextLike_bringInAccountV1 & { BuildContextLike_bringInAccountV1 & {
bundler: "vite" | "webpack";
extraThemeProperties: string[] | undefined; extraThemeProperties: string[] | undefined;
loginThemeResourcesFromKeycloakVersion: string; loginThemeResourcesFromKeycloakVersion: string;
projectBuildDirPath: string;
assetsDirPath: string;
urlPathname: string | undefined;
projectDirPath: string; projectDirPath: string;
keycloakifyBuildDirPath: string; projectBuildDirPath: string;
environmentVariables: { name: string; default: string }[]; environmentVariables: { name: string; default: string }[];
}; };
@ -58,9 +54,10 @@ assert<BuildContext extends BuildContextLike ? true : false>();
export async function generateSrcMainResourcesForMainTheme(params: { export async function generateSrcMainResourcesForMainTheme(params: {
themeName: string; themeName: string;
resourcesDirPath: string;
buildContext: BuildContextLike; buildContext: BuildContextLike;
}): Promise<void> { }): Promise<void> {
const { themeName, buildContext } = params; const { themeName, resourcesDirPath, buildContext } = params;
const { themeSrcDirPath } = getThemeSrcDirPath({ const { themeSrcDirPath } = getThemeSrcDirPath({
projectDirPath: buildContext.projectDirPath projectDirPath: buildContext.projectDirPath
@ -68,15 +65,7 @@ export async function generateSrcMainResourcesForMainTheme(params: {
const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => { const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => {
const { themeType } = params; const { themeType } = params;
return pathJoin( return pathJoin(resourcesDirPath, "theme", themeName, themeType);
buildContext.keycloakifyBuildDirPath,
"src",
"main",
"resources",
"theme",
themeName,
themeType
);
}; };
const cssGlobalsToDefine: Record<string, string> = {}; const cssGlobalsToDefine: Record<string, string> = {};
@ -207,8 +196,6 @@ export async function generateSrcMainResourcesForMainTheme(params: {
].forEach(pageId => { ].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId }); const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeTypeDirPath, { recursive: true });
fs.writeFileSync( fs.writeFileSync(
pathJoin(themeTypeDirPath, pageId), pathJoin(themeTypeDirPath, pageId),
Buffer.from(ftlCode, "utf8") Buffer.from(ftlCode, "utf8")
@ -291,6 +278,7 @@ export async function generateSrcMainResourcesForMainTheme(params: {
if (implementedThemeTypes.account) { if (implementedThemeTypes.account) {
await bringInAccountV1({ await bringInAccountV1({
resourcesDirPath,
buildContext buildContext
}); });
} }
@ -313,7 +301,7 @@ export async function generateSrcMainResourcesForMainTheme(params: {
} }
writeMetaInfKeycloakThemes({ writeMetaInfKeycloakThemes({
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath, resourcesDirPath,
metaInfKeycloakThemes metaInfKeycloakThemes
}); });
} }

View File

@ -2,7 +2,7 @@ import { join as pathJoin, extname as pathExtname, sep as pathSep } from "path";
import { transformCodebase } from "../../tools/transformCodebase"; import { transformCodebase } from "../../tools/transformCodebase";
import type { BuildContext } from "../../shared/buildContext"; import type { BuildContext } from "../../shared/buildContext";
import { import {
readMetaInfKeycloakThemes, readMetaInfKeycloakThemes_fromResourcesDirPath,
writeMetaInfKeycloakThemes writeMetaInfKeycloakThemes
} from "../../shared/metaInfKeycloakThemes"; } from "../../shared/metaInfKeycloakThemes";
import { assert } from "tsafe/assert"; import { assert } from "tsafe/assert";
@ -14,20 +14,13 @@ export type BuildContextLike = {
assert<BuildContext extends BuildContextLike ? true : false>(); assert<BuildContext extends BuildContextLike ? true : false>();
export function generateSrcMainResourcesForThemeVariant(params: { export function generateSrcMainResourcesForThemeVariant(params: {
resourcesDirPath: string;
themeName: string; themeName: string;
themeVariantName: string; themeVariantName: string;
buildContext: BuildContextLike;
}) { }) {
const { themeName, themeVariantName, buildContext } = params; const { resourcesDirPath, themeName, themeVariantName } = params;
const mainThemeDirPath = pathJoin( const mainThemeDirPath = pathJoin(resourcesDirPath, "theme", themeName);
buildContext.keycloakifyBuildDirPath,
"src",
"main",
"resources",
"theme",
themeName
);
transformCodebase({ transformCodebase({
srcDirPath: mainThemeDirPath, srcDirPath: mainThemeDirPath,
@ -57,9 +50,10 @@ export function generateSrcMainResourcesForThemeVariant(params: {
}); });
{ {
const updatedMetaInfKeycloakThemes = readMetaInfKeycloakThemes({ const updatedMetaInfKeycloakThemes =
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath readMetaInfKeycloakThemes_fromResourcesDirPath({
}); resourcesDirPath
});
updatedMetaInfKeycloakThemes.themes.push({ updatedMetaInfKeycloakThemes.themes.push({
name: themeVariantName, name: themeVariantName,
@ -73,7 +67,7 @@ export function generateSrcMainResourcesForThemeVariant(params: {
}); });
writeMetaInfKeycloakThemes({ writeMetaInfKeycloakThemes({
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath, resourcesDirPath,
metaInfKeycloakThemes: updatedMetaInfKeycloakThemes metaInfKeycloakThemes: updatedMetaInfKeycloakThemes
}); });
} }

View File

@ -1,74 +0,0 @@
import * as fs from "fs";
import {
join as pathJoin,
relative as pathRelative,
basename as pathBasename
} from "path";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../shared/buildContext";
import { accountV1ThemeName } from "../shared/constants";
export type BuildContextLike = {
keycloakifyBuildDirPath: string;
themeNames: string[];
};
assert<BuildContext extends BuildContextLike ? true : false>();
generateStartKeycloakTestingContainer.basename = "start_keycloak_testing_container.sh";
const containerName = "keycloak-testing-container";
const keycloakVersion = "24.0.4";
/** Files for being able to run a hot reload keycloak container */
export function generateStartKeycloakTestingContainer(params: {
jarFilePath: string;
doesImplementAccountTheme: boolean;
buildContext: BuildContextLike;
}) {
const { jarFilePath, doesImplementAccountTheme, buildContext } = params;
const themeRelativeDirPath = pathJoin("src", "main", "resources", "theme");
fs.writeFileSync(
pathJoin(
buildContext.keycloakifyBuildDirPath,
generateStartKeycloakTestingContainer.basename
),
Buffer.from(
[
"#!/usr/bin/env bash",
"",
`docker rm ${containerName} || true`,
"",
`cd "${buildContext.keycloakifyBuildDirPath}"`,
"",
"docker run \\",
" -p 8080:8080 \\",
` --name ${containerName} \\`,
" -e KEYCLOAK_ADMIN=admin \\",
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
` -v "${pathJoin(
"$(pwd)",
pathRelative(buildContext.keycloakifyBuildDirPath, jarFilePath)
)}":"/opt/keycloak/providers/${pathBasename(jarFilePath)}" \\`,
[
...(doesImplementAccountTheme ? [accountV1ThemeName] : []),
...buildContext.themeNames
].map(
themeName =>
` -v "${pathJoin(
"$(pwd)",
themeRelativeDirPath,
themeName
).replace(/\\/g, "/")}":"/opt/keycloak/themes/${themeName}":rw \\`
),
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
` start-dev`,
""
].join("\n"),
"utf8"
),
{ mode: 0o755 }
);
}

View File

@ -9,6 +9,7 @@ import type { CliCommandOptions } from "../main";
import chalk from "chalk"; import chalk from "chalk";
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion"; import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
import * as os from "os"; import * as os from "os";
import { rmSync } from "../tools/fs.rmSync";
export async function command(params: { cliCommandOptions: CliCommandOptions }) { export async function command(params: { cliCommandOptions: CliCommandOptions }) {
exit_if_maven_not_installed: { exit_if_maven_not_installed: {
@ -76,7 +77,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
); );
} }
await generateSrcMainResources({ buildContext }); const resourcesDirPath = pathJoin(buildContext.keycloakifyBuildDirPath, "resources");
await generateSrcMainResources({
resourcesDirPath,
buildContext
});
run_post_build_script: { run_post_build_script: {
if (buildContext.bundler !== "vite") { if (buildContext.bundler !== "vite") {
@ -84,7 +90,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
} }
child_process.execSync("npx vite", { child_process.execSync("npx vite", {
cwd: buildContext.projectDirPath, cwd: resourcesDirPath,
env: { env: {
...process.env, ...process.env,
[vitePluginSubScriptEnvNames.runPostBuildScript]: [vitePluginSubScriptEnvNames.runPostBuildScript]:
@ -98,7 +104,11 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
break build_jars; break build_jars;
} }
await buildJars({ buildContext }); await buildJars({ resourcesDirPath, buildContext });
}
if (Date.now() === 0) {
rmSync(resourcesDirPath, { recursive: true });
} }
console.log( console.log(

View File

@ -134,20 +134,6 @@ program
} }
}); });
program
.command({
name: "download-keycloak-default-theme",
description: "Download the built-in Keycloak theme."
})
.task({
skip,
handler: async cliCommandOptions => {
const { command } = await import("./download-keycloak-default-theme");
await command({ cliCommandOptions });
}
});
program program
.command({ .command({
name: "eject-page", name: "eject-page",

View File

@ -12,7 +12,7 @@ import { vitePluginSubScriptEnvNames } from "./constants";
export type BuildContext = { export type BuildContext = {
bundler: "vite" | "webpack"; bundler: "vite" | "webpack";
themeVersion: string; themeVersion: string;
themeNames: string[]; themeNames: [string, ...string[]];
extraThemeProperties: string[] | undefined; extraThemeProperties: string[] | undefined;
groupId: string; groupId: string;
artifactId: string; artifactId: string;
@ -147,7 +147,7 @@ export function getBuildContext(params: {
...resolvedViteConfig?.buildOptions ...resolvedViteConfig?.buildOptions
}; };
const themeNames = (() => { const themeNames = ((): [string, ...string[]] => {
if (buildOptions.themeName === undefined) { if (buildOptions.themeName === undefined) {
return [ return [
parsedPackageJson.name parsedPackageJson.name
@ -161,7 +161,11 @@ export function getBuildContext(params: {
return [buildOptions.themeName]; return [buildOptions.themeName];
} }
return buildOptions.themeName; const [mainThemeName, ...themeVariantNames] = buildOptions.themeName;
assert(mainThemeName !== undefined);
return [mainThemeName, ...themeVariantNames];
})(); })();
const projectBuildDirPath = (() => { const projectBuildDirPath = (() => {

View File

@ -1,50 +1,73 @@
import { join as pathJoin, dirname as pathDirname } from "path"; import { join as pathJoin, dirname as pathDirname } from "path";
import type { ThemeType } from "./constants"; import type { ThemeType } from "./constants";
import * as fs from "fs"; import * as fs from "fs";
import { assert } from "tsafe/assert";
import { extractArchive } from "../tools/extractArchive";
export type MetaInfKeycloakTheme = { export type MetaInfKeycloakTheme = {
themes: { name: string; types: (ThemeType | "email")[] }[]; themes: { name: string; types: (ThemeType | "email")[] }[];
}; };
export function getMetaInfKeycloakThemesJsonFilePath(params: { export function getMetaInfKeycloakThemesJsonFilePath(params: {
keycloakifyBuildDirPath: string; resourcesDirPath: string;
}) { }) {
const { keycloakifyBuildDirPath } = params; const { resourcesDirPath } = params;
return pathJoin( return pathJoin(
keycloakifyBuildDirPath === "." ? "" : keycloakifyBuildDirPath, resourcesDirPath === "." ? "" : resourcesDirPath,
"src",
"main",
"resources",
"META-INF", "META-INF",
"keycloak-themes.json" "keycloak-themes.json"
); );
} }
export function readMetaInfKeycloakThemes(params: { export function readMetaInfKeycloakThemes_fromResourcesDirPath(params: {
keycloakifyBuildDirPath: string; resourcesDirPath: string;
}): MetaInfKeycloakTheme { }) {
const { keycloakifyBuildDirPath } = params; const { resourcesDirPath } = params;
return JSON.parse( return JSON.parse(
fs fs
.readFileSync( .readFileSync(
getMetaInfKeycloakThemesJsonFilePath({ getMetaInfKeycloakThemesJsonFilePath({
keycloakifyBuildDirPath resourcesDirPath
}) })
) )
.toString("utf8") .toString("utf8")
) as MetaInfKeycloakTheme; ) as MetaInfKeycloakTheme;
} }
export async function readMetaInfKeycloakThemes_fromJar(params: {
jarFilePath: string;
}): Promise<MetaInfKeycloakTheme> {
const { jarFilePath } = params;
let metaInfKeycloakThemes: MetaInfKeycloakTheme | undefined = undefined;
await extractArchive({
archiveFilePath: jarFilePath,
onArchiveFile: async ({ relativeFilePathInArchive, readFile, earlyExit }) => {
if (
relativeFilePathInArchive ===
getMetaInfKeycloakThemesJsonFilePath({ resourcesDirPath: "." })
) {
metaInfKeycloakThemes = JSON.parse((await readFile()).toString("utf8"));
earlyExit();
}
}
});
assert(metaInfKeycloakThemes !== undefined);
return metaInfKeycloakThemes;
}
export function writeMetaInfKeycloakThemes(params: { export function writeMetaInfKeycloakThemes(params: {
keycloakifyBuildDirPath: string; resourcesDirPath: string;
metaInfKeycloakThemes: MetaInfKeycloakTheme; metaInfKeycloakThemes: MetaInfKeycloakTheme;
}) { }) {
const { keycloakifyBuildDirPath, metaInfKeycloakThemes } = params; const { resourcesDirPath, metaInfKeycloakThemes } = params;
const metaInfKeycloakThemesJsonPath = getMetaInfKeycloakThemesJsonFilePath({ const metaInfKeycloakThemesJsonPath = getMetaInfKeycloakThemesJsonFilePath({
keycloakifyBuildDirPath resourcesDirPath
}); });
{ {

View File

@ -2,7 +2,7 @@ import { getBuildContext } from "../shared/buildContext";
import { exclude } from "tsafe/exclude"; import { exclude } from "tsafe/exclude";
import type { CliCommandOptions as CliCommandOptions_common } from "../main"; import type { CliCommandOptions as CliCommandOptions_common } from "../main";
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion"; import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
import { readMetaInfKeycloakThemes } from "../shared/metaInfKeycloakThemes"; import { readMetaInfKeycloakThemes_fromJar } from "../shared/metaInfKeycloakThemes";
import { accountV1ThemeName, containerName } from "../shared/constants"; import { accountV1ThemeName, containerName } from "../shared/constants";
import { SemVer } from "../tools/SemVer"; import { SemVer } from "../tools/SemVer";
import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange"; import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange";
@ -21,6 +21,9 @@ import * as runExclusive from "run-exclusive";
import { extractArchive } from "../tools/extractArchive"; import { extractArchive } from "../tools/extractArchive";
import { appBuild } from "./appBuild"; import { appBuild } from "./appBuild";
import { keycloakifyBuild } from "./keycloakifyBuild"; import { keycloakifyBuild } from "./keycloakifyBuild";
import { isInside } from "../tools/isInside";
import { existsAsync } from "../tools/fs.existsAsync";
import { rm } from "../tools/fs.rm";
export type CliCommandOptions = CliCommandOptions_common & { export type CliCommandOptions = CliCommandOptions_common & {
port: number; port: number;
@ -112,13 +115,31 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
} }
} }
const metaInfKeycloakThemes = readMetaInfKeycloakThemes({ const { doesImplementAccountTheme } = await (async () => {
keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath const latestJarFilePath = fs
}); .readdirSync(buildContext.keycloakifyBuildDirPath)
.filter(fileBasename => fileBasename.endsWith(".jar"))
.map(fileBasename =>
pathJoin(buildContext.keycloakifyBuildDirPath, fileBasename)
)
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0];
const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some( assert(latestJarFilePath !== undefined);
({ name }) => name === accountV1ThemeName
); const metaInfKeycloakThemes = await readMetaInfKeycloakThemes_fromJar({
jarFilePath: latestJarFilePath
});
const mainThemeEntry = metaInfKeycloakThemes.themes.find(
({ name }) => name === buildContext.themeNames[0]
);
assert(mainThemeEntry !== undefined);
const doesImplementAccountTheme = mainThemeEntry.types.includes("account");
return { doesImplementAccountTheme };
})();
const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } = const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } =
await (async function getKeycloakMajor(): Promise<{ await (async function getKeycloakMajor(): Promise<{
@ -262,65 +283,30 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
const jarFilePath = pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename); const jarFilePath = pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename);
const { doUseBuiltInAccountV1Theme } = await (async () => { async function extractThemeResourcesFromJar() {
let doUseBuiltInAccountV1Theme = false;
await extractArchive({ await extractArchive({
archiveFilePath: jarFilePath, archiveFilePath: jarFilePath,
onArchiveFile: async ({ relativeFilePathInArchive, readFile, earlyExit }) => { onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) => {
for (const themeName of buildContext.themeNames) { if (isInside({ dirPath: "theme", filePath: relativeFilePathInArchive })) {
if ( await writeFile({
relativeFilePathInArchive === filePath: pathJoin(
pathJoin("theme", themeName, "account", "theme.properties") buildContext.keycloakifyBuildDirPath,
) { relativeFilePathInArchive
if ( )
(await readFile()) });
.toString("utf8")
.includes("parent=keycloak")
) {
doUseBuiltInAccountV1Theme = true;
}
earlyExit();
}
} }
} }
}); });
}
return { doUseBuiltInAccountV1Theme }; {
})(); const destDirPath = pathJoin(buildContext.keycloakifyBuildDirPath, "theme");
if (await existsAsync(destDirPath)) {
await rm(destDirPath, { recursive: true });
}
}
const accountThemePropertyPatch = !doUseBuiltInAccountV1Theme await extractThemeResourcesFromJar();
? undefined
: () => {
for (const themeName of buildContext.themeNames) {
const filePath = pathJoin(
buildContext.keycloakifyBuildDirPath,
"src",
"main",
"resources",
"theme",
themeName,
"account",
"theme.properties"
);
const sourceCode = fs.readFileSync(filePath);
const modifiedSourceCode = Buffer.from(
sourceCode
.toString("utf8")
.replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
"utf8"
);
assert(Buffer.compare(modifiedSourceCode, sourceCode) !== 0);
fs.writeFileSync(filePath, modifiedSourceCode);
}
};
accountThemePropertyPatch?.();
try { try {
child_process.execSync(`docker rm --force ${containerName}`, { child_process.execSync(`docker rm --force ${containerName}`, {
@ -348,14 +334,19 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
: []), : []),
...[ ...[
...buildContext.themeNames, ...buildContext.themeNames,
...(doUseBuiltInAccountV1Theme ? [] : [accountV1ThemeName]) ...(fs.existsSync(
pathJoin(
buildContext.keycloakifyBuildDirPath,
"theme",
accountV1ThemeName
)
)
? [accountV1ThemeName]
: [])
] ]
.map(themeName => ({ .map(themeName => ({
localDirPath: pathJoin( localDirPath: pathJoin(
buildContext.keycloakifyBuildDirPath, buildContext.keycloakifyBuildDirPath,
"src",
"main",
"resources",
"theme", "theme",
themeName themeName
), ),
@ -459,7 +450,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
return; return;
} }
accountThemePropertyPatch?.(); await extractThemeResourcesFromJar();
console.log(chalk.green("Theme rebuilt and updated in Keycloak.")); console.log(chalk.green("Theme rebuilt and updated in Keycloak."));
}); });