Allow running of UI development server against local build quarkus distribution of keycloak (#23918)

Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Alice 2023-10-20 09:53:16 -04:00 committed by GitHub
parent f2dc57bfe1
commit 21b2de4a28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 12 deletions

View file

@ -18,5 +18,15 @@ pnpm run start
This will download the [Nightly version](https://github.com/keycloak/keycloak/releases/tag/nightly) of the Keycloak server and run it locally on port `8180`. If a previously downloaded version was found in the `server/` directory then that one will be used instead. If you want to download the latest Nightly version you can remove the server directory before running the command to start the server. This will download the [Nightly version](https://github.com/keycloak/keycloak/releases/tag/nightly) of the Keycloak server and run it locally on port `8180`. If a previously downloaded version was found in the `server/` directory then that one will be used instead. If you want to download the latest Nightly version you can remove the server directory before running the command to start the server.
If you want to run with a local Quarkus distribution of Keycloak for development purposes, you can do so by running this command instead:
```bash
pnpm run start -- --local
```
**All other arguments will be passed through to the underlying Keycloak server.**
In order for the development version of the Admin UI to work you will have to import a custom client to the Keycloak server. This is only required during development as the development server for the Admin UI runs on a different port. This client will be imported automatically under the name `security-admin-console-v2` when the Keycloak server starts. In order for the development version of the Admin UI to work you will have to import a custom client to the Keycloak server. This is only required during development as the development server for the Admin UI runs on a different port. This client will be imported automatically under the name `security-admin-console-v2` when the Keycloak server starts.
This client only allows redirects from/to "localhost:8080" so be sure either modify the client json in `./scripts` or only attempt to authenticate and redirect from that address

View file

@ -3,7 +3,8 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "wireit", "start": "wireit",
"clear-data": "rm -r ./server/data" "delete-data": "rm -r ./server/data",
"delete-server": "rm -r ./server"
}, },
"wireit": { "wireit": {
"start": { "start": {

View file

@ -9,30 +9,39 @@ import path from "node:path";
import { pipeline } from "node:stream/promises"; import { pipeline } from "node:stream/promises";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { extract } from "tar-fs"; import { extract } from "tar-fs";
import { parseArgs } from "node:util";
const DIR_NAME = path.dirname(fileURLToPath(import.meta.url)); const DIR_NAME = path.dirname(fileURLToPath(import.meta.url));
const SERVER_DIR = path.resolve(DIR_NAME, "../server"); const SERVER_DIR = path.resolve(DIR_NAME, "../server");
const LOCAL_QUARKUS = path.resolve(DIR_NAME, "../../../../quarkus/dist/target");
const LOCAL_DIST_NAME = "keycloak-999.0.0-SNAPSHOT.tar.gz";
const SCRIPT_EXTENSION = process.platform === "win32" ? ".bat" : ".sh"; const SCRIPT_EXTENSION = process.platform === "win32" ? ".bat" : ".sh";
const ADMIN_USERNAME = "admin"; const ADMIN_USERNAME = "admin";
const ADMIN_PASSWORD = "admin"; const ADMIN_PASSWORD = "admin";
const AUTH_DELAY = 5000; const AUTH_DELAY = 10000;
const AUTH_RETRY_LIMIT = 3; const AUTH_RETRY_LIMIT = 3;
const options = {
local: {
type: "boolean",
},
};
await startServer(); await startServer();
async function startServer() { async function startServer() {
await downloadServer(); let { scriptArgs, keycloakArgs } = handleArgs(process.argv.slice(2));
await downloadServer(scriptArgs.local);
console.info("Starting server…"); console.info("Starting server…");
const args = process.argv.slice(2);
const child = spawn( const child = spawn(
path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`), path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`),
[ [
"start-dev", "start-dev",
"--http-port=8180", "--http-port=8180",
"--features=account3,admin-fine-grained-authz,declarative-user-profile", "--features=account3,admin-fine-grained-authz,declarative-user-profile",
...args, ...keycloakArgs,
], ],
{ {
env: { env: {
@ -50,7 +59,27 @@ async function startServer() {
await importClient(); await importClient();
} }
async function downloadServer() { function handleArgs(args) {
const { values, tokens } = parseArgs({
args,
options,
strict: false,
tokens: true,
});
// we need to remove the args that belong to the script so that we can pass the rest through to keycloak
tokens
.filter((token) => Object.hasOwn(options, token.name))
.forEach((token) => {
let tokenRaw = token.rawName;
if (token.value) {
tokenRaw += `=${token.value}`;
}
args.splice(args.indexOf(tokenRaw), 1);
});
return { scriptArgs: values, keycloakArgs: args };
}
async function downloadServer(local) {
const directoryExists = fs.existsSync(SERVER_DIR); const directoryExists = fs.existsSync(SERVER_DIR);
if (directoryExists) { if (directoryExists) {
@ -58,11 +87,17 @@ async function downloadServer() {
return; return;
} }
let assetStream;
if (local) {
console.info(`Looking for ${LOCAL_DIST_NAME} at ${LOCAL_QUARKUS}`);
assetStream = fs.createReadStream(
path.join(LOCAL_QUARKUS, LOCAL_DIST_NAME),
);
} else {
console.info("Downloading and extracting server…"); console.info("Downloading and extracting server…");
const nightlyAsset = await getNightlyAsset(); const nightlyAsset = await getNightlyAsset();
const assetStream = await getAssetAsStream(nightlyAsset); assetStream = await getAssetAsStream(nightlyAsset);
}
await extractTarball(assetStream, SERVER_DIR, { strip: 1 }); await extractTarball(assetStream, SERVER_DIR, { strip: 1 });
} }