2024-05-13 03:35:24 +02:00
|
|
|
import { assert, type Equals } from "tsafe/assert";
|
2024-05-12 21:41:49 +02:00
|
|
|
import type { KeycloakAccountV1Version, KeycloakThemeAdditionalInfoExtensionVersion } from "./extensionVersions";
|
2024-05-13 03:35:24 +02:00
|
|
|
import { join as pathJoin, dirname as pathDirname } from "path";
|
2024-05-13 00:40:16 +02:00
|
|
|
import { transformCodebase } from "../../tools/transformCodebase";
|
2024-05-15 05:14:01 +02:00
|
|
|
import type { BuildOptions } from "../../shared/buildOptions";
|
2024-05-13 00:40:16 +02:00
|
|
|
import * as fs from "fs/promises";
|
2024-05-15 05:14:01 +02:00
|
|
|
import { accountV1ThemeName } from "../../shared/constants";
|
2024-05-13 00:40:16 +02:00
|
|
|
import { generatePom, BuildOptionsLike as BuildOptionsLike_generatePom } from "./generatePom";
|
2024-05-17 00:54:54 +02:00
|
|
|
import { readFileSync } from "fs";
|
2024-05-13 00:40:16 +02:00
|
|
|
import { isInside } from "../../tools/isInside";
|
|
|
|
import child_process from "child_process";
|
2024-05-17 00:54:54 +02:00
|
|
|
import { rmSync } from "../../tools/fs.rmSync";
|
2024-05-13 00:40:16 +02:00
|
|
|
|
|
|
|
export type BuildOptionsLike = BuildOptionsLike_generatePom & {
|
|
|
|
keycloakifyBuildDirPath: string;
|
|
|
|
themeNames: string[];
|
|
|
|
artifactId: string;
|
|
|
|
themeVersion: string;
|
2024-05-17 00:54:54 +02:00
|
|
|
cacheDirPath: string;
|
2024-05-13 00:40:16 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
2024-05-12 19:16:59 +02:00
|
|
|
|
|
|
|
export async function buildJar(params: {
|
|
|
|
jarFileBasename: string;
|
2024-05-12 21:41:49 +02:00
|
|
|
keycloakAccountV1Version: KeycloakAccountV1Version;
|
|
|
|
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
|
2024-05-13 00:40:16 +02:00
|
|
|
buildOptions: BuildOptionsLike;
|
2024-05-12 19:16:59 +02:00
|
|
|
}): Promise<void> {
|
2024-05-13 00:40:16 +02:00
|
|
|
const { jarFileBasename, keycloakAccountV1Version, keycloakThemeAdditionalInfoExtensionVersion, buildOptions } = params;
|
|
|
|
|
2024-05-17 00:54:54 +02:00
|
|
|
const keycloakifyBuildTmpDirPath = pathJoin(buildOptions.cacheDirPath, jarFileBasename.replace(".jar", ""));
|
2024-05-13 00:40:16 +02:00
|
|
|
|
2024-05-17 00:54:54 +02:00
|
|
|
rmSync(keycloakifyBuildTmpDirPath, { "recursive": true, "force": true });
|
2024-05-13 23:21:27 +02:00
|
|
|
|
2024-05-13 03:35:24 +02:00
|
|
|
{
|
2024-05-17 01:01:58 +02:00
|
|
|
const keycloakThemesJsonFilePath = pathJoin("src", "main", "resources", "META-INF", "keycloak-themes.json");
|
2024-05-13 00:40:16 +02:00
|
|
|
|
|
|
|
const themePropertiesFilePathSet = new Set(
|
2024-05-17 01:01:58 +02:00
|
|
|
...buildOptions.themeNames.map(themeName => pathJoin("src", "main", "resources", "theme", themeName, "account", "theme.properties"))
|
2024-05-13 00:40:16 +02:00
|
|
|
);
|
|
|
|
|
2024-05-17 01:01:58 +02:00
|
|
|
const accountV1RelativeDirPath = pathJoin("src", "main", "resources", "theme", accountV1ThemeName);
|
2024-05-13 00:40:16 +02:00
|
|
|
|
|
|
|
transformCodebase({
|
|
|
|
"srcDirPath": buildOptions.keycloakifyBuildDirPath,
|
|
|
|
"destDirPath": keycloakifyBuildTmpDirPath,
|
|
|
|
"transformSourceCode":
|
|
|
|
keycloakAccountV1Version !== null
|
|
|
|
? undefined
|
|
|
|
: ({ fileRelativePath, sourceCode }) => {
|
|
|
|
if (fileRelativePath === keycloakThemesJsonFilePath) {
|
|
|
|
const keycloakThemesJsonParsed = JSON.parse(sourceCode.toString("utf8")) as {
|
|
|
|
themes: { name: string; types: string[] }[];
|
|
|
|
};
|
|
|
|
|
|
|
|
keycloakThemesJsonParsed.themes = keycloakThemesJsonParsed.themes.filter(({ name }) => name !== accountV1ThemeName);
|
|
|
|
|
|
|
|
return { "modifiedSourceCode": Buffer.from(JSON.stringify(keycloakThemesJsonParsed, null, 2), "utf8") };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isInside({ "dirPath": "target", "filePath": fileRelativePath })) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isInside({ "dirPath": accountV1RelativeDirPath, "filePath": fileRelativePath })) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (themePropertiesFilePathSet.has(fileRelativePath)) {
|
|
|
|
return {
|
|
|
|
"modifiedSourceCode": Buffer.from(
|
|
|
|
sourceCode.toString("utf8").replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
|
|
|
|
"utf8"
|
|
|
|
)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return { "modifiedSourceCode": sourceCode };
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-13 03:35:24 +02:00
|
|
|
route_legacy_pages: {
|
|
|
|
// NOTE: If there's no account theme there is no special target for keycloak 24 and up so we create
|
|
|
|
// the pages anyway. If there is an account pages, since we know that account-v1 is only support keycloak
|
|
|
|
// 24 in version 0.4 and up, we can safely break the route for legacy pages.
|
|
|
|
const doBreak: boolean = (() => {
|
|
|
|
switch (keycloakAccountV1Version) {
|
|
|
|
case null:
|
|
|
|
return false;
|
|
|
|
case "0.3":
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
if (doBreak) {
|
|
|
|
break route_legacy_pages;
|
|
|
|
}
|
|
|
|
|
|
|
|
(["register.ftl", "login-update-profile.ftl"] as const).forEach(pageId =>
|
|
|
|
buildOptions.themeNames.map(themeName => {
|
2024-05-17 01:01:58 +02:00
|
|
|
const ftlFilePath = pathJoin(keycloakifyBuildTmpDirPath, "src", "main", "resources", "theme", themeName, "login", pageId);
|
2024-05-13 03:35:24 +02:00
|
|
|
|
|
|
|
const ftlFileContent = readFileSync(ftlFilePath).toString("utf8");
|
|
|
|
|
|
|
|
const realPageId = (() => {
|
|
|
|
switch (pageId) {
|
|
|
|
case "register.ftl":
|
|
|
|
return "register-user-profile.ftl";
|
|
|
|
case "login-update-profile.ftl":
|
|
|
|
return "update-user-profile.ftl";
|
|
|
|
}
|
|
|
|
assert<Equals<typeof pageId, never>>(false);
|
|
|
|
})();
|
|
|
|
|
|
|
|
const modifiedFtlFileContent = ftlFileContent.replace(
|
2024-05-13 23:21:27 +02:00
|
|
|
`out["pageId"] = "\${pageId}";`,
|
2024-05-13 03:35:24 +02:00
|
|
|
`out["pageId"] = "${pageId}"; out["realPageId"] = "${realPageId}";`
|
|
|
|
);
|
|
|
|
|
|
|
|
assert(modifiedFtlFileContent !== ftlFileContent);
|
|
|
|
|
|
|
|
fs.writeFile(pathJoin(pathDirname(ftlFilePath), realPageId), Buffer.from(modifiedFtlFileContent, "utf8"));
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-13 00:40:16 +02:00
|
|
|
{
|
|
|
|
const { pomFileCode } = generatePom({
|
|
|
|
buildOptions,
|
|
|
|
keycloakAccountV1Version,
|
|
|
|
keycloakThemeAdditionalInfoExtensionVersion
|
|
|
|
});
|
|
|
|
|
2024-05-13 23:39:18 +02:00
|
|
|
await fs.writeFile(pathJoin(keycloakifyBuildTmpDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8"));
|
2024-05-13 00:40:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
await new Promise<void>((resolve, reject) =>
|
|
|
|
child_process.exec("mvn clean install", { "cwd": keycloakifyBuildTmpDirPath }, error => {
|
|
|
|
if (error !== null) {
|
2024-05-13 23:39:18 +02:00
|
|
|
console.error(
|
|
|
|
`Build jar failed: ${JSON.stringify(
|
|
|
|
{
|
|
|
|
jarFileBasename,
|
|
|
|
keycloakAccountV1Version,
|
|
|
|
keycloakThemeAdditionalInfoExtensionVersion
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
2
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
|
2024-05-13 00:40:16 +02:00
|
|
|
reject(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
resolve();
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
await fs.rename(
|
|
|
|
pathJoin(keycloakifyBuildTmpDirPath, "target", `${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`),
|
|
|
|
pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)
|
|
|
|
);
|
|
|
|
|
2024-05-17 00:54:54 +02:00
|
|
|
rmSync(keycloakifyBuildTmpDirPath, { "recursive": true });
|
2024-05-12 19:16:59 +02:00
|
|
|
}
|