Bundle JAR WPI
This commit is contained in:
parent
c6cf564842
commit
7d8ae040fd
@ -17,15 +17,20 @@ import { isInside } from "../../tools/isInside";
|
||||
import child_process from "child_process";
|
||||
import { rmSync } from "../../tools/fs.rmSync";
|
||||
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
|
||||
import {
|
||||
bundleExtensionsIntoJar,
|
||||
type BuildContextLike as BuildContextLike_bundleExtensionsIntoJar
|
||||
} from "./bundleExtensionsIntoJar";
|
||||
|
||||
export type BuildContextLike = BuildContextLike_generatePom & {
|
||||
export type BuildContextLike = BuildContextLike_generatePom &
|
||||
BuildContextLike_bundleExtensionsIntoJar & {
|
||||
keycloakifyBuildDirPath: string;
|
||||
themeNames: string[];
|
||||
artifactId: string;
|
||||
themeVersion: string;
|
||||
cacheDirPath: string;
|
||||
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||
};
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
@ -234,12 +239,19 @@ export async function buildJar(params: {
|
||||
)
|
||||
);
|
||||
|
||||
await fs.rename(
|
||||
pathJoin(
|
||||
const jarFilePath_generatedByMaven = pathJoin(
|
||||
keycloakifyBuildCacheDirPath,
|
||||
"target",
|
||||
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
|
||||
),
|
||||
);
|
||||
|
||||
await bundleExtensionsIntoJar({
|
||||
buildContext,
|
||||
jarFilePath: jarFilePath_generatedByMaven
|
||||
});
|
||||
|
||||
await fs.rename(
|
||||
jarFilePath_generatedByMaven,
|
||||
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
|
||||
);
|
||||
}
|
||||
|
137
src/bin/keycloakify/buildJars/bundleExtensionsIntoJar.ts
Normal file
137
src/bin/keycloakify/buildJars/bundleExtensionsIntoJar.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { downloadAndExtractArchive } from "../../tools/downloadAndExtractArchive";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
import { join as pathJoin, basename as pathBasename, sep as pathSep } from "path";
|
||||
import { rm } from "../../tools/fs.rm";
|
||||
import { extractArchive } from "../../tools/extractArchive";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export type BuildContextLike = {
|
||||
cacheDirPath: string;
|
||||
fetchOptions: BuildContext["fetchOptions"];
|
||||
extensionJars: BuildContext["extensionJars"];
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
|
||||
export async function bundleExtensionsIntoJar(params: {
|
||||
jarFilePath: string;
|
||||
buildContext: BuildContextLike;
|
||||
}): Promise<void> {
|
||||
const { jarFilePath, buildContext } = params;
|
||||
|
||||
if (buildContext.extensionJars.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mergeDirPath = pathJoin(
|
||||
buildContext.cacheDirPath,
|
||||
`merge_${pathBasename(jarFilePath).replace(/\.jar$/, "")}_${crypto
|
||||
.createHash("sha256")
|
||||
.update(jarFilePath)
|
||||
.digest("hex")
|
||||
.substring(0, 5)}`
|
||||
);
|
||||
|
||||
await extractArchive({
|
||||
archiveFilePath: jarFilePath,
|
||||
onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) =>
|
||||
writeFile({
|
||||
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive)
|
||||
})
|
||||
});
|
||||
|
||||
for (const extensionJar of buildContext.extensionJars) {
|
||||
const transformSourceCode = (params: {
|
||||
fileRelativePath: string;
|
||||
sourceCode: Buffer;
|
||||
}): { modifiedSourceCode: Buffer } | undefined => {
|
||||
const { fileRelativePath } = params;
|
||||
|
||||
if (!fileRelativePath.startsWith(`META-INF${pathSep}`)) {
|
||||
for (const ext of [".DSA", ".SF", ".RSA"]) {
|
||||
if (fileRelativePath.endsWith(ext)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
switch (extensionJar.type) {
|
||||
case "path":
|
||||
await extractArchive({
|
||||
archiveFilePath: extensionJar.path,
|
||||
onArchiveFile: async ({
|
||||
relativeFilePathInArchive,
|
||||
writeFile,
|
||||
readFile
|
||||
}) => {
|
||||
const transformResult = transformSourceCode({
|
||||
fileRelativePath: relativeFilePathInArchive,
|
||||
sourceCode: await readFile()
|
||||
});
|
||||
|
||||
if (transformResult === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
await writeFile({
|
||||
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive),
|
||||
modifiedData: transformResult.modifiedSourceCode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case "url": {
|
||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||
url: extensionJar.url,
|
||||
cacheDirPath: buildContext.cacheDirPath,
|
||||
fetchOptions: buildContext.fetchOptions,
|
||||
uniqueIdOfOnArchiveFile: "noOp",
|
||||
onArchiveFile: async ({ fileRelativePath, writeFile }) =>
|
||||
writeFile({ fileRelativePath })
|
||||
});
|
||||
transformCodebase({
|
||||
srcDirPath: extractedDirPath,
|
||||
destDirPath: mergeDirPath,
|
||||
transformSourceCode
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
transformCodebase({
|
||||
srcDirPath: extractedDirPath,
|
||||
destDirPath: mergeDirPath,
|
||||
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
|
||||
if (fileRelativePath === pathJoin("META-INF", "MANIFEST.MF")) {
|
||||
const sourceCodeStr = sourceCode.toString("utf8");
|
||||
|
||||
const lines = sourceCodeStr.split(/\r?\n/);
|
||||
|
||||
console.log(lines);
|
||||
|
||||
return {
|
||||
modifiedSourceCode: Buffer.concat([
|
||||
sourceCode,
|
||||
Buffer.from(
|
||||
`Class-Path: ${pathBasename(userProvidedJarFilePathOrUrl)}\n`
|
||||
)
|
||||
])
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
// TODO: Acctually build new jar
|
||||
|
||||
await rm(mergeDirPath, { recursive: true, force: true });
|
||||
}
|
@ -314,8 +314,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
}
|
||||
|
||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||
urlOrPath:
|
||||
"https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
||||
url: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
||||
cacheDirPath: buildContext.cacheDirPath,
|
||||
fetchOptions: buildContext.fetchOptions,
|
||||
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
||||
|
@ -26,6 +26,8 @@ import { type ThemeType } from "./constants";
|
||||
import { id } from "tsafe/id";
|
||||
import chalk from "chalk";
|
||||
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
|
||||
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
|
||||
import { same } from "evt/tools/inDepth/same";
|
||||
|
||||
export type BuildContext = {
|
||||
themeVersion: string;
|
||||
@ -61,6 +63,7 @@ export type BuildContext = {
|
||||
keycloakVersionRange: KeycloakVersionRange;
|
||||
jarFileBasename: string;
|
||||
}[];
|
||||
extensionJars: ({ type: "path"; path: string } | { type: "url"; url: string })[];
|
||||
startKeycloakOptions: {
|
||||
dockerImage:
|
||||
| {
|
||||
@ -88,6 +91,7 @@ export type BuildOptions = {
|
||||
loginThemeResourcesFromKeycloakVersion?: string;
|
||||
keycloakifyBuildDirPath?: string;
|
||||
kcContextExclusionsFtl?: string;
|
||||
extensionJars?: string[];
|
||||
startKeycloakOptions?: {
|
||||
dockerImage?: string;
|
||||
dockerExtraArgs?: string[];
|
||||
@ -360,6 +364,7 @@ export function getBuildContext(params: {
|
||||
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
||||
keycloakifyBuildDirPath: z.string().optional(),
|
||||
kcContextExclusionsFtl: z.string().optional(),
|
||||
extensionJars: z.array(z.string()).optional(),
|
||||
startKeycloakOptions: zStartKeycloakOptions.optional()
|
||||
}),
|
||||
zAccountThemeImplAndKeycloakVersionTargets
|
||||
@ -520,6 +525,36 @@ export function getBuildContext(params: {
|
||||
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
||||
})();
|
||||
|
||||
const buildForKeycloakMajorVersionNumber = (() => {
|
||||
const envValue = process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
|
||||
|
||||
if (envValue === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const major = parseInt(envValue);
|
||||
|
||||
assert(!isNaN(major));
|
||||
|
||||
return major;
|
||||
})();
|
||||
|
||||
function urlOrPathToDiscriminatingWrapper(
|
||||
urlOrPath: string
|
||||
): { type: "url"; url: string } | { type: "path"; path: string } {
|
||||
if (/^https?:\/\//.test(urlOrPath)) {
|
||||
return { type: "url", url: urlOrPath };
|
||||
}
|
||||
|
||||
return {
|
||||
type: "path",
|
||||
path: getAbsoluteAndInOsFormatPath({
|
||||
pathIsh: urlOrPath,
|
||||
cwd: projectDirPath
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
bundler,
|
||||
packageJsonFilePath,
|
||||
@ -717,21 +752,6 @@ export function getBuildContext(params: {
|
||||
`keycloak-theme-for-kc-${range}.jar`;
|
||||
|
||||
build_for_specific_keycloak_major_version: {
|
||||
const buildForKeycloakMajorVersionNumber = (() => {
|
||||
const envValue =
|
||||
process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
|
||||
|
||||
if (envValue === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const major = parseInt(envValue);
|
||||
|
||||
assert(!isNaN(major));
|
||||
|
||||
return major;
|
||||
})();
|
||||
|
||||
if (buildForKeycloakMajorVersionNumber === undefined) {
|
||||
break build_for_specific_keycloak_major_version;
|
||||
}
|
||||
@ -931,6 +951,10 @@ export function getBuildContext(params: {
|
||||
|
||||
return jarTargets;
|
||||
})(),
|
||||
extensionJars: (buildForKeycloakMajorVersionNumber !== undefined
|
||||
? []
|
||||
: buildOptions.extensionJars ?? []
|
||||
).map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath)),
|
||||
startKeycloakOptions: {
|
||||
dockerImage: (() => {
|
||||
if (buildOptions.startKeycloakOptions?.dockerImage === undefined) {
|
||||
@ -949,21 +973,14 @@ export function getBuildContext(params: {
|
||||
})(),
|
||||
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
|
||||
})
|
||||
};
|
||||
}
|
||||
),
|
||||
extensionJars: [
|
||||
...(buildForKeycloakMajorVersionNumber !== undefined
|
||||
? buildOptions.extensionJars ?? []
|
||||
: []),
|
||||
...(buildOptions.startKeycloakOptions?.extensionJars ?? [])
|
||||
]
|
||||
.map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath))
|
||||
.reduce(...removeDuplicates<BuildContext["extensionJars"][number]>(same)),
|
||||
realmJsonFilePath:
|
||||
buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined
|
||||
? undefined
|
||||
|
@ -21,7 +21,7 @@ export async function downloadKeycloakDefaultTheme(params: {
|
||||
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
|
||||
|
||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||
urlOrPath: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
||||
url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
|
||||
cacheDirPath: buildContext.cacheDirPath,
|
||||
fetchOptions: buildContext.fetchOptions,
|
||||
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",
|
||||
|
@ -200,7 +200,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
const { archiveFilePath } = await downloadAndExtractArchive({
|
||||
cacheDirPath: buildContext.cacheDirPath,
|
||||
fetchOptions: buildContext.fetchOptions,
|
||||
urlOrPath: extensionJar.url,
|
||||
url: extensionJar.url,
|
||||
uniqueIdOfOnArchiveFile: "no extraction",
|
||||
onArchiveFile: async () => {}
|
||||
});
|
||||
|
@ -1,15 +1,14 @@
|
||||
import fetch, { type FetchOptions } from "make-fetch-happen";
|
||||
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
|
||||
import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path";
|
||||
import { dirname as pathDirname, join as pathJoin } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { extractArchive } from "./extractArchive";
|
||||
import { existsAsync } from "./fs.existsAsync";
|
||||
import * as crypto from "crypto";
|
||||
import { rm } from "./fs.rm";
|
||||
import * as fsPr from "fs/promises";
|
||||
|
||||
export async function downloadAndExtractArchive(params: {
|
||||
urlOrPath: string;
|
||||
url: string;
|
||||
uniqueIdOfOnArchiveFile: string;
|
||||
onArchiveFile: (params: {
|
||||
fileRelativePath: string;
|
||||
@ -22,33 +21,16 @@ export async function downloadAndExtractArchive(params: {
|
||||
cacheDirPath: string;
|
||||
fetchOptions: FetchOptions | undefined;
|
||||
}): Promise<{ extractedDirPath: string; archiveFilePath: string }> {
|
||||
const {
|
||||
urlOrPath,
|
||||
uniqueIdOfOnArchiveFile,
|
||||
onArchiveFile,
|
||||
cacheDirPath,
|
||||
fetchOptions
|
||||
} = params;
|
||||
const { url, uniqueIdOfOnArchiveFile, onArchiveFile, cacheDirPath, fetchOptions } =
|
||||
params;
|
||||
|
||||
const isUrl = /^https?:\/\//.test(urlOrPath);
|
||||
|
||||
const archiveFileBasename = isUrl
|
||||
? urlOrPath.split("?")[0].split("/").reverse()[0]
|
||||
: pathBasename(urlOrPath);
|
||||
const archiveFileBasename = url.split("?")[0].split("/").reverse()[0];
|
||||
|
||||
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
|
||||
|
||||
download: {
|
||||
await mkdir(pathDirname(archiveFilePath), { recursive: true });
|
||||
|
||||
if (!isUrl) {
|
||||
await fsPr.copyFile(urlOrPath, archiveFilePath);
|
||||
|
||||
break download;
|
||||
}
|
||||
|
||||
const url = urlOrPath;
|
||||
|
||||
if (await existsAsync(archiveFilePath)) {
|
||||
const isDownloaded = await SuccessTracker.getIsDownloaded({
|
||||
cacheDirPath,
|
||||
|
Loading…
x
Reference in New Issue
Block a user