From 98f3de55f2c6c6c15e30b26c9e950de6ff414b43 Mon Sep 17 00:00:00 2001 From: Stan Lewis Date: Fri, 18 Oct 2024 14:09:39 -0400 Subject: [PATCH] feat(cli): add flag to specify output directory This change adds an --export-to flag to the package-dynamic-plugins CLI command. This flag will skip the container build step, and instead copy the staged dynamic plugin assets to the specified output directory. The addition of this flag makes the --tag argument optional, and only required if --export-to is not specified. Signed-off-by: Stan Lewis --- .changeset/mean-eagles-dress.md | 7 ++ packages/cli/src/commands/index.ts | 8 ++- .../package-dynamic-plugins/command.ts | 64 +++++++++++++------ 3 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 .changeset/mean-eagles-dress.md diff --git a/.changeset/mean-eagles-dress.md b/.changeset/mean-eagles-dress.md new file mode 100644 index 00000000000..c781be2071e --- /dev/null +++ b/.changeset/mean-eagles-dress.md @@ -0,0 +1,7 @@ +--- +"@janus-idp/cli": minor +--- + +feat(cli) add flag to specify output directory + +This change adds an --export-to flag to the package-dynamic-plugins CLI command. This flag will skip the container build step, and instead copy the staged dynamic plugin assets to the specified output directory. The addition of this flag makes the --tag argument optional, and only required if --export-to is not specified. diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 741d8b609e5..aa74f4099c9 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -161,9 +161,13 @@ export function registerScriptCommand(program: Command) { '--preserve-temp-dir', 'Leave the temporary staging directory on the filesystem instead of deleting it', ) - .requiredOption( + .option( + '--export-to ', + 'Export the plugins to the specified directory, skips building the container image', + ) + .option( '-t, --tag ', - 'Tag name to use when building the plugin registry image', + 'Tag name to use when building the plugin registry image. Required if "--export-to" is not specified', ) .option( '--use-docker', diff --git a/packages/cli/src/commands/package-dynamic-plugins/command.ts b/packages/cli/src/commands/package-dynamic-plugins/command.ts index 159fbe6f2a8..c7183f24b65 100644 --- a/packages/cli/src/commands/package-dynamic-plugins/command.ts +++ b/packages/cli/src/commands/package-dynamic-plugins/command.ts @@ -12,18 +12,25 @@ import { paths } from '../../lib/paths'; import { Task } from '../../lib/tasks'; export async function command(opts: OptionValues): Promise { - const { forceExport, preserveTempDir, tag, useDocker } = opts; - const containerTool = useDocker ? 'docker' : 'podman'; - // check if the container tool is available - try { - await Task.forCommand(`${containerTool} --version`); - } catch (e) { + const { exportTo, forceExport, preserveTempDir, tag, useDocker } = opts; + if (!exportTo && !tag) { Task.error( - `Unable to find ${containerTool} command: ${e}\nMake sure that ${containerTool} is installed and available in your PATH.`, + `Neither ${chalk.white('--export-to')} or ${chalk.white('--tag')} was specified, either specify ${chalk.white('--export-to')} to export plugins to a directory or ${chalk.white('--tag')} to export plugins to a container image`, ); return; } - + const containerTool = useDocker ? 'docker' : 'podman'; + // check if the container tool is available, skip if just exporting the plugins to a directory + if (!exportTo) { + try { + await Task.forCommand(`${containerTool} --version`); + } catch (e) { + Task.error( + `Unable to find ${containerTool} command: ${e}\nMake sure that ${containerTool} is installed and available in your PATH.`, + ); + return; + } + } const workspacePackage = await fs.readJson( paths.resolveTarget('package.json'), ); @@ -95,7 +102,12 @@ export async function command(opts: OptionValues): Promise { const targetDirectory = path.join(tmpDir, packageName); Task.log(`Copying '${distDirectory}' to '${targetDirectory}`); try { - fs.cpSync(distDirectory, targetDirectory, { recursive: true }); + // Copy the exported package to the staging area and ensure symlinks + // are copied as normal folders + fs.cpSync(distDirectory, targetDirectory, { + recursive: true, + dereference: true, + }); const { name, version, @@ -156,18 +168,29 @@ export async function command(opts: OptionValues): Promise { metadataFile, JSON.stringify(pluginRegistryMetadata, undefined, 2), ); - // run the command to generate the image - Task.log(`Creating image using ${containerTool}`); - await Task.forCommand( - `echo "from scratch + if (exportTo) { + // copy the temporary directory contents to the target directory + fs.mkdirSync(exportTo, { recursive: true }); + Task.log(`Writing exported plugins to ${exportTo}`); + fs.readdirSync(tmpDir).forEach(entry => { + const source = path.join(tmpDir, entry); + const destination = path.join(exportTo, entry); + fs.copySync(source, destination, { recursive: true, overwrite: true }); + }); + } else { + // run the command to generate the image + Task.log(`Creating image using ${containerTool}`); + await Task.forCommand( + `echo "from scratch COPY . . " | ${containerTool} build --annotation com.redhat.rhdh.plugins='${JSON.stringify(pluginRegistryMetadata)}' -t '${tag}' -f - . `, - { cwd: tmpDir }, - ); - Task.log(`Successfully built image ${tag} with following plugins:`); - for (const plugin of pluginRegistryMetadata) { - Task.log(` ${chalk.white(Object.keys(plugin)[0])}`); + { cwd: tmpDir }, + ); + Task.log(`Successfully built image ${tag} with following plugins:`); + for (const plugin of pluginRegistryMetadata) { + Task.log(` ${chalk.white(Object.keys(plugin)[0])}`); + } } // print out a configuration example based on available plugin data try { @@ -175,8 +198,11 @@ COPY . . plugins: pluginRegistryMetadata.map(plugin => { const packageName = Object.keys(plugin)[0]; const pluginConfig = pluginConfigs[packageName]; + const packageString = exportTo + ? `./local-plugins/${packageName}` + : `oci://${tag}!${packageName}`; return { - package: `oci://${tag}!${packageName}`, + package: packageString, disabled: false, ...(pluginConfig ? { pluginConfig } : {}), };