Make hot reloading when testing account theme with older Keycloak version work
This commit is contained in:
@ -1,6 +1,5 @@
|
|||||||
import { skipBuildJarsEnvName } from "../shared/constants";
|
import { skipBuildJarsEnvName } from "../shared/constants";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import chalk from "chalk";
|
|
||||||
import { Deferred } from "evt/tools/Deferred";
|
import { Deferred } from "evt/tools/Deferred";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { BuildOptions } from "../shared/buildOptions";
|
import type { BuildOptions } from "../shared/buildOptions";
|
||||||
@ -34,15 +33,6 @@ export async function keycloakifyBuild(params: {
|
|||||||
|
|
||||||
child.stderr.on("data", data => process.stderr.write(data));
|
child.stderr.on("data", data => process.stderr.write(data));
|
||||||
|
|
||||||
child.on("exit", code => {
|
|
||||||
if (code !== 0) {
|
|
||||||
console.log(chalk.yellow("Theme not updated, build failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.green("Rebuild done"));
|
|
||||||
});
|
|
||||||
|
|
||||||
child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
|
child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
|
||||||
|
|
||||||
const { isSuccess } = await dResult.pr;
|
const { isSuccess } = await dResult.pr;
|
||||||
|
@ -2,11 +2,7 @@ import { readBuildOptions } from "../shared/buildOptions";
|
|||||||
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 } from "../shared/metaInfKeycloakThemes";
|
||||||
import {
|
import { accountV1ThemeName, containerName } from "../shared/constants";
|
||||||
accountV1ThemeName,
|
|
||||||
skipBuildJarsEnvName,
|
|
||||||
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";
|
||||||
import { getJarFileBasename } from "../shared/getJarFileBasename";
|
import { getJarFileBasename } from "../shared/getJarFileBasename";
|
||||||
@ -18,11 +14,12 @@ import chalk from "chalk";
|
|||||||
import chokidar from "chokidar";
|
import chokidar from "chokidar";
|
||||||
import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
|
import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
|
||||||
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
|
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
|
||||||
import { Deferred } from "evt/tools/Deferred";
|
|
||||||
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
||||||
import cliSelect from "cli-select";
|
import cliSelect from "cli-select";
|
||||||
import * as runExclusive from "run-exclusive";
|
import * as runExclusive from "run-exclusive";
|
||||||
import { extractArchive } from "../tools/extractArchive";
|
import { extractArchive } from "../tools/extractArchive";
|
||||||
|
import { appBuild } from "./appBuild";
|
||||||
|
import { keycloakifyBuild } from "./keycloakifyBuild";
|
||||||
|
|
||||||
export type CliCommandOptions = CliCommandOptions_common & {
|
export type CliCommandOptions = CliCommandOptions_common & {
|
||||||
port: number;
|
port: number;
|
||||||
@ -85,18 +82,34 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
const buildOptions = readBuildOptions({ cliCommandOptions });
|
const buildOptions = readBuildOptions({ cliCommandOptions });
|
||||||
|
|
||||||
exit_if_theme_not_built: {
|
{
|
||||||
if (fs.existsSync(buildOptions.keycloakifyBuildDirPath)) {
|
const { isAppBuildSuccess } = await appBuild({
|
||||||
break exit_if_theme_not_built;
|
doSkipIfReactAppBuildDirExists: true,
|
||||||
|
buildOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isAppBuildSuccess) {
|
||||||
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
`App build failed, exiting. Try running 'yarn build-keycloak-theme' and see what's wrong.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
const { isKeycloakifyBuildSuccess } = await keycloakifyBuild({
|
||||||
[
|
doSkipBuildJars: false,
|
||||||
`${chalk.red("The theme has not been built.")}`,
|
buildOptions
|
||||||
`Please run ${chalk.bold("npx vite && npx keycloakify build")} first.`
|
});
|
||||||
].join(" ")
|
|
||||||
);
|
if (!isKeycloakifyBuildSuccess) {
|
||||||
process.exit(1);
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
`Keycloakify build failed, exiting. Try running 'yarn build-keycloak-theme' and see what's wrong.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
|
const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
|
||||||
@ -255,40 +268,65 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
|
|
||||||
const jarFilePath = pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename);
|
const jarFilePath = pathJoin(buildOptions.keycloakifyBuildDirPath, jarFileBasename);
|
||||||
|
|
||||||
let doLinkAccountV1Theme = false;
|
const { doUseBuiltInAccountV1Theme } = await (async () => {
|
||||||
|
let doUseBuiltInAccountV1Theme = false;
|
||||||
|
|
||||||
await extractArchive({
|
await extractArchive({
|
||||||
archiveFilePath: jarFilePath,
|
archiveFilePath: jarFilePath,
|
||||||
onArchiveFile: async ({ relativeFilePathInArchive, readFile, writeFile }) => {
|
onArchiveFile: async ({ relativeFilePathInArchive, readFile, earlyExit }) => {
|
||||||
for (const themeName of buildOptions.themeNames) {
|
for (const themeName of buildOptions.themeNames) {
|
||||||
if (
|
|
||||||
relativeFilePathInArchive ===
|
|
||||||
pathJoin("theme", themeName, "account", "theme.properties")
|
|
||||||
) {
|
|
||||||
if (
|
if (
|
||||||
(await readFile())
|
relativeFilePathInArchive ===
|
||||||
.toString("utf8")
|
pathJoin("theme", themeName, "account", "theme.properties")
|
||||||
.includes(`parent=${accountV1ThemeName}`)
|
|
||||||
) {
|
) {
|
||||||
doLinkAccountV1Theme = true;
|
if (
|
||||||
}
|
(await readFile())
|
||||||
|
.toString("utf8")
|
||||||
|
.includes(`parent=keycloak`)
|
||||||
|
) {
|
||||||
|
doUseBuiltInAccountV1Theme = true;
|
||||||
|
}
|
||||||
|
|
||||||
await writeFile({
|
earlyExit();
|
||||||
filePath: pathJoin(
|
}
|
||||||
buildOptions.keycloakifyBuildDirPath,
|
|
||||||
"src",
|
|
||||||
"main",
|
|
||||||
"resources",
|
|
||||||
"theme",
|
|
||||||
themeName,
|
|
||||||
"account",
|
|
||||||
"theme.properties"
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
return { doUseBuiltInAccountV1Theme };
|
||||||
|
})();
|
||||||
|
|
||||||
|
const accountThemePropertyPatch = !doUseBuiltInAccountV1Theme
|
||||||
|
? undefined
|
||||||
|
: () => {
|
||||||
|
for (const themeName of buildOptions.themeNames) {
|
||||||
|
const filePath = pathJoin(
|
||||||
|
buildOptions.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?.();
|
||||||
|
|
||||||
const spawnArgs = [
|
const spawnArgs = [
|
||||||
"docker",
|
"docker",
|
||||||
@ -389,67 +427,30 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const runBuildKeycloakTheme = runExclusive.build(async () => {
|
const runFullBuild = runExclusive.build(async () => {
|
||||||
console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
|
console.log(chalk.cyan("Detected changes in the theme. Rebuilding ..."));
|
||||||
|
|
||||||
{
|
const { isAppBuildSuccess } = await appBuild({
|
||||||
const dResult = new Deferred<{ isSuccess: boolean }>();
|
doSkipIfReactAppBuildDirExists: false,
|
||||||
|
buildOptions
|
||||||
|
});
|
||||||
|
|
||||||
const child = child_process.spawn("npx", ["vite", "build"], {
|
if (!isAppBuildSuccess) {
|
||||||
cwd: buildOptions.reactAppRootDirPath
|
return;
|
||||||
});
|
|
||||||
|
|
||||||
child.stdout.on("data", data => {
|
|
||||||
if (data.toString("utf8").includes("gzip:")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.stdout.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
child.stderr.on("data", data => process.stderr.write(data));
|
|
||||||
|
|
||||||
child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
|
|
||||||
|
|
||||||
const { isSuccess } = await dResult.pr;
|
|
||||||
|
|
||||||
if (!isSuccess) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
const { isKeycloakifyBuildSuccess } = await keycloakifyBuild({
|
||||||
const dResult = new Deferred<{ isSuccess: boolean }>();
|
doSkipBuildJars: true,
|
||||||
|
buildOptions
|
||||||
|
});
|
||||||
|
|
||||||
const child = child_process.spawn("npx", ["keycloakify", "build"], {
|
if (!isKeycloakifyBuildSuccess) {
|
||||||
cwd: buildOptions.reactAppRootDirPath,
|
return;
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
[skipBuildJarsEnvName]: "true"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
child.stdout.on("data", data => process.stdout.write(data));
|
|
||||||
|
|
||||||
child.stderr.on("data", data => process.stderr.write(data));
|
|
||||||
|
|
||||||
child.on("exit", code => {
|
|
||||||
if (code !== 0) {
|
|
||||||
console.log(chalk.yellow("Theme not updated, build failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.green("Rebuild done"));
|
|
||||||
});
|
|
||||||
|
|
||||||
child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
|
|
||||||
|
|
||||||
const { isSuccess } = await dResult.pr;
|
|
||||||
|
|
||||||
if (!isSuccess) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountThemePropertyPatch?.();
|
||||||
|
|
||||||
|
console.log(chalk.green("Theme rebuilt and updated in Keycloak."));
|
||||||
});
|
});
|
||||||
|
|
||||||
const { waitForDebounce } = waitForDebounceFactory({ delay: 400 });
|
const { waitForDebounce } = waitForDebounceFactory({ delay: 400 });
|
||||||
@ -461,7 +462,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
|||||||
.on("all", async () => {
|
.on("all", async () => {
|
||||||
await waitForDebounce();
|
await waitForDebounce();
|
||||||
|
|
||||||
runBuildKeycloakTheme();
|
runFullBuild();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ export async function extractArchive(params: {
|
|||||||
relativeFilePathInArchive: string;
|
relativeFilePathInArchive: string;
|
||||||
readFile: () => Promise<Buffer>;
|
readFile: () => Promise<Buffer>;
|
||||||
writeFile: (params: { filePath: string; modifiedData?: Buffer }) => Promise<void>;
|
writeFile: (params: { filePath: string; modifiedData?: Buffer }) => Promise<void>;
|
||||||
|
earlyExit: () => void;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
}) {
|
}) {
|
||||||
const { archiveFilePath, onArchiveFile } = params;
|
const { archiveFilePath, onArchiveFile } = params;
|
||||||
@ -107,7 +108,11 @@ export async function extractArchive(params: {
|
|||||||
await onArchiveFile({
|
await onArchiveFile({
|
||||||
relativeFilePathInArchive: entry.fileName.split("/").join(pathSep),
|
relativeFilePathInArchive: entry.fileName.split("/").join(pathSep),
|
||||||
readFile: () => readFile(entry),
|
readFile: () => readFile(entry),
|
||||||
writeFile: params => writeFile(entry, params)
|
writeFile: params => writeFile(entry, params),
|
||||||
|
earlyExit: () => {
|
||||||
|
zipFile.close();
|
||||||
|
dDone.resolve();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user