309 lines
10 KiB
TypeScript
Raw Normal View History

import { assert, type Equals } from "tsafe/assert";
2024-05-20 15:48:51 +02:00
import type {
KeycloakAccountV1Version,
KeycloakThemeAdditionalInfoExtensionVersion
} from "./extensionVersions";
import { join as pathJoin, dirname as pathDirname } from "path";
2024-05-13 00:40:16 +02:00
import { transformCodebase } from "../../tools/transformCodebase";
import type { BuildOptions } from "../../shared/buildOptions";
2024-05-13 00:40:16 +02:00
import * as fs from "fs/promises";
import { accountV1ThemeName } from "../../shared/constants";
2024-05-20 15:48:51 +02:00
import {
generatePom,
BuildOptionsLike as BuildOptionsLike_generatePom
} from "./generatePom";
import { readFileSync } from "fs";
2024-05-13 00:40:16 +02:00
import { isInside } from "../../tools/isInside";
import child_process from "child_process";
import { rmSync } from "../../tools/fs.rmSync";
2024-05-20 02:25:45 +02:00
import { getMetaInfKeycloakThemesJsonFilePath } from "../../shared/metaInfKeycloakThemes";
import type { Param0 } from "tsafe";
2024-05-13 00:40:16 +02:00
export type BuildOptionsLike = BuildOptionsLike_generatePom & {
keycloakifyBuildDirPath: string;
themeNames: string[];
artifactId: string;
themeVersion: string;
cacheDirPath: string;
2024-05-13 00:40:16 +02:00
};
assert<BuildOptions extends BuildOptionsLike ? true : false>();
export async function buildJar(params: {
jarFileBasename: string;
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
2024-05-13 00:40:16 +02:00
buildOptions: BuildOptionsLike;
}): Promise<void> {
2024-05-20 15:48:51 +02:00
const {
jarFileBasename,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
buildOptions
} = params;
const keycloakifyBuildTmpDirPath = pathJoin(
buildOptions.cacheDirPath,
jarFileBasename.replace(".jar", "")
);
2024-05-13 00:40:16 +02:00
2024-05-20 15:48:51 +02:00
rmSync(keycloakifyBuildTmpDirPath, { recursive: true, force: true });
2024-05-13 23:21:27 +02:00
{
2024-05-20 15:48:51 +02:00
const metaInfKeycloakThemesJsonRelativePath =
getMetaInfKeycloakThemesJsonFilePath({
keycloakifyBuildDirPath: ""
});
2024-05-13 00:40:16 +02:00
2024-05-20 02:25:45 +02:00
const { transformCodebase_common } = (() => {
2024-05-20 15:48:51 +02:00
const includingAccountV1ThemeNames = [
...buildOptions.themeNames,
accountV1ThemeName
];
const transformCodebase_common: Param0<
typeof transformCodebase
>["transformSourceCode"] = ({ fileRelativePath, sourceCode }) => {
2024-05-20 02:25:45 +02:00
if (metaInfKeycloakThemesJsonRelativePath === fileRelativePath) {
2024-05-20 15:48:51 +02:00
return { modifiedSourceCode: sourceCode };
2024-05-20 02:25:45 +02:00
}
for (const themeName of includingAccountV1ThemeNames) {
2024-05-20 15:48:51 +02:00
if (
isInside({
dirPath: pathJoin(
"src",
"main",
"resources",
"theme",
themeName
),
filePath: fileRelativePath
})
) {
return { modifiedSourceCode: sourceCode };
2024-05-20 02:25:45 +02:00
}
}
return undefined;
};
return { transformCodebase_common };
})();
const { transformCodebase_patchForUsingBuiltinAccountV1 } = (() => {
if (keycloakAccountV1Version !== null) {
2024-05-20 15:48:51 +02:00
return {
transformCodebase_patchForUsingBuiltinAccountV1: undefined
};
2024-05-20 02:25:45 +02:00
}
const themePropertiesFileRelativePathSet = new Set(
2024-05-26 12:19:47 +02:00
buildOptions.themeNames.map(themeName =>
2024-05-20 15:48:51 +02:00
pathJoin(
"src",
"main",
"resources",
"theme",
themeName,
"account",
"theme.properties"
)
)
2024-05-20 02:25:45 +02:00
);
2024-05-20 15:48:51 +02:00
const accountV1RelativeDirPath = pathJoin(
"src",
"main",
"resources",
"theme",
accountV1ThemeName
);
2024-05-20 02:25:45 +02:00
2024-05-20 15:48:51 +02:00
const transformCodebase_patchForUsingBuiltinAccountV1: Param0<
typeof transformCodebase
>["transformSourceCode"] = ({ fileRelativePath, sourceCode }) => {
if (
isInside({
dirPath: accountV1RelativeDirPath,
filePath: fileRelativePath
})
) {
2024-05-20 02:25:45 +02:00
return undefined;
}
if (fileRelativePath === metaInfKeycloakThemesJsonRelativePath) {
2024-05-20 15:48:51 +02:00
const keycloakThemesJsonParsed = JSON.parse(
sourceCode.toString("utf8")
) as {
2024-05-20 02:25:45 +02:00
themes: { name: string; types: string[] }[];
};
2024-05-20 15:48:51 +02:00
keycloakThemesJsonParsed.themes =
keycloakThemesJsonParsed.themes.filter(
({ name }) => name !== accountV1ThemeName
);
2024-05-20 02:25:45 +02:00
2024-05-20 15:48:51 +02:00
return {
modifiedSourceCode: Buffer.from(
JSON.stringify(keycloakThemesJsonParsed, null, 2),
"utf8"
)
};
2024-05-20 02:25:45 +02:00
}
if (themePropertiesFileRelativePathSet.has(fileRelativePath)) {
const modifiedSourceCode = Buffer.from(
2024-05-20 15:48:51 +02:00
sourceCode
.toString("utf8")
.replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
2024-05-20 02:25:45 +02:00
"utf8"
);
assert(Buffer.compare(modifiedSourceCode, sourceCode) !== 0);
return { modifiedSourceCode };
}
2024-05-20 15:48:51 +02:00
return { modifiedSourceCode: sourceCode };
2024-05-20 02:25:45 +02:00
};
return { transformCodebase_patchForUsingBuiltinAccountV1 };
})();
2024-05-13 00:40:16 +02:00
transformCodebase({
2024-05-20 15:48:51 +02:00
srcDirPath: buildOptions.keycloakifyBuildDirPath,
destDirPath: keycloakifyBuildTmpDirPath,
transformSourceCode: params => {
2024-05-20 02:25:45 +02:00
const resultCommon = transformCodebase_common(params);
if (resultCommon === undefined) {
return undefined;
}
if (transformCodebase_patchForUsingBuiltinAccountV1 === undefined) {
return resultCommon;
}
const { modifiedSourceCode, newFileName } = resultCommon;
assert(newFileName === undefined);
return transformCodebase_patchForUsingBuiltinAccountV1?.({
...params,
2024-05-20 15:48:51 +02:00
sourceCode: modifiedSourceCode
2024-05-20 02:25:45 +02:00
});
}
2024-05-13 00:40:16 +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-20 15:48:51 +02:00
const ftlFilePath = pathJoin(
keycloakifyBuildTmpDirPath,
"src",
"main",
"resources",
"theme",
themeName,
"login",
pageId
);
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}";`,
`out["pageId"] = "${pageId}"; out["realPageId"] = "${realPageId}";`
);
assert(modifiedFtlFileContent !== ftlFileContent);
2024-05-20 15:48:51 +02:00
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-20 15:48:51 +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) =>
2024-05-20 15:48:51 +02:00
child_process.exec(
`mvn clean install -Dmaven.repo.local=${pathJoin(keycloakifyBuildTmpDirPath, ".m2")}`,
2024-05-20 15:48:51 +02:00
{ cwd: keycloakifyBuildTmpDirPath },
error => {
if (error !== null) {
console.error(
`Build jar failed: ${JSON.stringify(
{
jarFileBasename,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion
},
null,
2
)}`
);
2024-05-13 23:39:18 +02:00
2024-05-20 15:48:51 +02:00
reject(error);
return;
}
resolve();
2024-05-13 00:40:16 +02:00
}
2024-05-20 15:48:51 +02:00
)
2024-05-13 00:40:16 +02:00
);
await fs.rename(
2024-05-20 15:48:51 +02:00
pathJoin(
keycloakifyBuildTmpDirPath,
"target",
`${buildOptions.artifactId}-${buildOptions.themeVersion}.jar`
),
2024-05-13 00:40:16 +02:00
pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename)
);
2024-05-20 15:48:51 +02:00
rmSync(keycloakifyBuildTmpDirPath, { recursive: true });
}