From 077aa5d2e160a9641aae9e53f8f8cdb054a42072 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 7 Feb 2025 14:24:57 +0100 Subject: [PATCH] cli/new: move template execution impl to subdir Signed-off-by: Patrik Oldsberg --- .../cli/src/commands/versions/migrate.test.ts | 2 +- .../new/collection/collectTemplateParams.ts | 2 +- packages/cli/src/lib/new/createNewPackage.ts | 115 ++------------- .../TemporaryDirectoryManager.ts | 0 .../new/{ => execution}/additionalActions.ts | 4 +- .../executePluginPackageTemplate.test.ts} | 2 +- .../executePluginPackageTemplate.ts} | 10 +- .../src/lib/new/execution/executeTemplate.ts | 131 ++++++++++++++++++ .../src/lib/new/{ => execution}/testUtils.ts | 2 +- .../src/lib/new/{ => execution}/utils.test.ts | 2 +- .../cli/src/lib/new/{ => execution}/utils.ts | 2 +- 11 files changed, 154 insertions(+), 118 deletions(-) rename packages/cli/src/lib/new/{ => execution}/TemporaryDirectoryManager.ts (100%) rename packages/cli/src/lib/new/{ => execution}/additionalActions.ts (98%) rename packages/cli/src/lib/new/{executeTemplate.test.ts => execution/executePluginPackageTemplate.test.ts} (98%) rename packages/cli/src/lib/new/{executeTemplate.ts => execution/executePluginPackageTemplate.ts} (95%) create mode 100644 packages/cli/src/lib/new/execution/executeTemplate.ts rename packages/cli/src/lib/new/{ => execution}/testUtils.ts (98%) rename packages/cli/src/lib/new/{ => execution}/utils.test.ts (98%) rename packages/cli/src/lib/new/{ => execution}/utils.ts (98%) diff --git a/packages/cli/src/commands/versions/migrate.test.ts b/packages/cli/src/commands/versions/migrate.test.ts index bfaa103e98..ba8195ce3f 100644 --- a/packages/cli/src/commands/versions/migrate.test.ts +++ b/packages/cli/src/commands/versions/migrate.test.ts @@ -21,7 +21,7 @@ import * as run from '../../lib/run'; import migrate from './migrate'; import { withLogCollector } from '@backstage/test-utils'; import fs from 'fs-extra'; -import { expectLogsToMatch } from '../../lib/new/testUtils'; +import { expectLogsToMatch } from '../../lib/new/execution/testUtils'; // Remove log coloring to simplify log matching jest.mock('chalk', () => ({ diff --git a/packages/cli/src/lib/new/collection/collectTemplateParams.ts b/packages/cli/src/lib/new/collection/collectTemplateParams.ts index 39d7c0635a..6af95d511d 100644 --- a/packages/cli/src/lib/new/collection/collectTemplateParams.ts +++ b/packages/cli/src/lib/new/collection/collectTemplateParams.ts @@ -19,7 +19,7 @@ import { paths } from '../../paths'; import { NewConfig } from '../config/types'; import { promptOptions } from './prompts'; import { Template } from '../types'; -import { Options } from '../utils'; +import { Options } from '../execution/utils'; type CollectTemplateParamsOptions = { config: NewConfig; diff --git a/packages/cli/src/lib/new/createNewPackage.ts b/packages/cli/src/lib/new/createNewPackage.ts index cb1d85908e..b631d458a8 100644 --- a/packages/cli/src/lib/new/createNewPackage.ts +++ b/packages/cli/src/lib/new/createNewPackage.ts @@ -14,22 +14,10 @@ * limitations under the License. */ -import camelCase from 'lodash/camelCase'; -import upperFirst from 'lodash/upperFirst'; -import { isMonoRepo } from '@backstage/cli-node'; -import { assertError } from '@backstage/errors'; - -import { paths } from '../paths'; -import { Task } from '../tasks'; -import { addCodeownersEntry } from '../codeowners'; - -import { createDirName, resolvePackageName } from './utils'; -import { runAdditionalActions } from './additionalActions'; -import { executePluginPackageTemplate } from './executeTemplate'; -import { TemporaryDirectoryManager } from './TemporaryDirectoryManager'; -import { loadNewConfig } from './config/loadNewConfig'; -import { NewTemplateLoader } from './loader/NewTemplateLoader'; import { collectTemplateParams } from './collection/collectTemplateParams'; +import { loadNewConfig } from './config/loadNewConfig'; +import { executeNewTemplate } from './execution/executeTemplate'; +import { NewTemplateLoader } from './loader/NewTemplateLoader'; export type CreateNewPackageOptions = { preselectedTemplateId?: string; @@ -44,107 +32,24 @@ export type CreateNewPackageOptions = { }; export async function createNewPackage(options: CreateNewPackageOptions) { - const newConfig = await loadNewConfig(); + const config = await loadNewConfig(); const selectedTemplate = await NewTemplateLoader.selectTemplateInteractively( - newConfig, + config, options.preselectedTemplateId, ); const template = await NewTemplateLoader.loadTemplate(selectedTemplate); const params = await collectTemplateParams({ - config: newConfig, + config, template, globals: options.globals, prefilledParams: options.prefilledParams, }); - const tmpDirManager = TemporaryDirectoryManager.create(); - - const dirName = createDirName(template, params); - const targetDir = paths.resolveTargetRoot(params.targetPath, dirName); - - const packageName = resolvePackageName({ - baseName: dirName, - scope: params.scope, - plugin: template.plugin ?? true, + await executeNewTemplate({ + config, + template, + params, }); - - const moduleVar = - params.moduleId ?? - `${camelCase(params.id)}Module${camelCase( - params.moduleId, - )[0].toUpperCase()}${camelCase(params.moduleId).slice(1)}`; // used in default-backend-module template - const extensionName = `${upperFirst(camelCase(params.id))}Page`; // used in default-plugin template - const pluginVar = `${camelCase(params.id)}Plugin`; // used in default-backend-plugin and default-plugin template - - let modified = false; - try { - await executePluginPackageTemplate( - { - isMonoRepo: await isMonoRepo(), - createTemporaryDirectory: tmpDirManager.createDir, - markAsModified() { - modified = true; - }, - }, - { - targetDir, - templateDir: template.templatePath, - values: { - name: packageName, - privatePackage: params.private, - pluginVersion: params.baseVersion, - moduleVar, - extensionName, - pluginVar, - ...params, - }, - }, - ); - - if (template.additionalActions?.length) { - await runAdditionalActions(template.additionalActions, { - name: packageName, - version: params.baseVersion, - id: params.id, // for frontend legacy - extensionName, // for frontend legacy - }); - } - - if (params.owner) { - await addCodeownersEntry(targetDir, params.owner); - } - - await Task.forCommand('yarn install', { - cwd: targetDir, - optional: true, - }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - - Task.log(); - Task.log(`🎉 Successfully created ${template.id}`); - Task.log(); - } catch (error) { - assertError(error); - Task.error(error.message); - - if (modified) { - Task.log('It seems that something went wrong in the creation process 🤔'); - Task.log(); - Task.log( - 'We have left the changes that were made intact in case you want to', - ); - Task.log( - 'continue manually, but you can also revert the changes and try again.', - ); - - Task.error(`🔥 Failed to create ${template.id}!`); - } - } finally { - tmpDirManager.cleanup(); - } } diff --git a/packages/cli/src/lib/new/TemporaryDirectoryManager.ts b/packages/cli/src/lib/new/execution/TemporaryDirectoryManager.ts similarity index 100% rename from packages/cli/src/lib/new/TemporaryDirectoryManager.ts rename to packages/cli/src/lib/new/execution/TemporaryDirectoryManager.ts diff --git a/packages/cli/src/lib/new/additionalActions.ts b/packages/cli/src/lib/new/execution/additionalActions.ts similarity index 98% rename from packages/cli/src/lib/new/additionalActions.ts rename to packages/cli/src/lib/new/execution/additionalActions.ts index bd668172f5..8041f12c79 100644 --- a/packages/cli/src/lib/new/additionalActions.ts +++ b/packages/cli/src/lib/new/execution/additionalActions.ts @@ -14,8 +14,8 @@ * limitations under the License. */ import fs from 'fs-extra'; -import { paths } from '../paths'; -import { Task } from '../tasks'; +import { paths } from '../../paths'; +import { Task } from '../../tasks'; interface AdditionalActionsOptions { name: string; diff --git a/packages/cli/src/lib/new/executeTemplate.test.ts b/packages/cli/src/lib/new/execution/executePluginPackageTemplate.test.ts similarity index 98% rename from packages/cli/src/lib/new/executeTemplate.test.ts rename to packages/cli/src/lib/new/execution/executePluginPackageTemplate.test.ts index 24de81b4b0..79c9fa9483 100644 --- a/packages/cli/src/lib/new/executeTemplate.test.ts +++ b/packages/cli/src/lib/new/execution/executePluginPackageTemplate.test.ts @@ -19,7 +19,7 @@ import { mockPaths } from './testUtils'; import { executePluginPackageTemplate, templatingTask, -} from './executeTemplate'; +} from './executePluginPackageTemplate'; import { createMockDirectory } from '@backstage/backend-test-utils'; describe('executePluginPackageTemplate', () => { diff --git a/packages/cli/src/lib/new/executeTemplate.ts b/packages/cli/src/lib/new/execution/executePluginPackageTemplate.ts similarity index 95% rename from packages/cli/src/lib/new/executeTemplate.ts rename to packages/cli/src/lib/new/execution/executePluginPackageTemplate.ts index 131d6344c2..259d1ae928 100644 --- a/packages/cli/src/lib/new/executeTemplate.ts +++ b/packages/cli/src/lib/new/execution/executePluginPackageTemplate.ts @@ -25,11 +25,11 @@ import { relative as relativePath, } from 'path'; -import { paths } from '../paths'; -import { Task } from '../tasks'; -import { Lockfile } from '../versioning'; -import { createPackageVersionProvider } from '../version'; -import { CreateContext } from './types'; +import { paths } from '../../paths'; +import { Task } from '../../tasks'; +import { Lockfile } from '../../versioning'; +import { createPackageVersionProvider } from '../../version'; +import { CreateContext } from '../types'; export async function executePluginPackageTemplate( ctx: CreateContext, diff --git a/packages/cli/src/lib/new/execution/executeTemplate.ts b/packages/cli/src/lib/new/execution/executeTemplate.ts new file mode 100644 index 0000000000..319a74350a --- /dev/null +++ b/packages/cli/src/lib/new/execution/executeTemplate.ts @@ -0,0 +1,131 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import camelCase from 'lodash/camelCase'; +import upperFirst from 'lodash/upperFirst'; +import { isMonoRepo } from '@backstage/cli-node'; +import { assertError } from '@backstage/errors'; + +import { paths } from '../../paths'; +import { Task } from '../../tasks'; +import { addCodeownersEntry } from '../../codeowners'; + +import { createDirName, resolvePackageName } from './utils'; +import { runAdditionalActions } from './additionalActions'; +import { executePluginPackageTemplate } from './executePluginPackageTemplate'; +import { TemporaryDirectoryManager } from './TemporaryDirectoryManager'; +import { NewConfig } from '../config/types'; +import { Template } from '../types'; +import { Options } from './utils'; + +type ExecuteNewTemplateOptions = { + config: NewConfig; + template: Template; + params: Options; +}; + +export async function executeNewTemplate(options: ExecuteNewTemplateOptions) { + const { template, params } = options; + + const tmpDirManager = TemporaryDirectoryManager.create(); + + const dirName = createDirName(template, params); + const targetDir = paths.resolveTargetRoot(params.targetPath, dirName); + + const packageName = resolvePackageName({ + baseName: dirName, + scope: params.scope, + plugin: template.plugin ?? true, + }); + + const moduleVar = + params.moduleId ?? + `${camelCase(params.id)}Module${camelCase( + params.moduleId, + )[0].toUpperCase()}${camelCase(params.moduleId).slice(1)}`; // used in default-backend-module template + const extensionName = `${upperFirst(camelCase(params.id))}Page`; // used in default-plugin template + const pluginVar = `${camelCase(params.id)}Plugin`; // used in default-backend-plugin and default-plugin template + + let modified = false; + try { + await executePluginPackageTemplate( + { + isMonoRepo: await isMonoRepo(), + createTemporaryDirectory: tmpDirManager.createDir, + markAsModified() { + modified = true; + }, + }, + { + targetDir, + templateDir: template.templatePath, + values: { + name: packageName, + privatePackage: params.private, + pluginVersion: params.baseVersion, + moduleVar, + extensionName, + pluginVar, + ...params, + }, + }, + ); + + if (template.additionalActions?.length) { + await runAdditionalActions(template.additionalActions, { + name: packageName, + version: params.baseVersion, + id: params.id, // for frontend legacy + extensionName, // for frontend legacy + }); + } + + if (params.owner) { + await addCodeownersEntry(targetDir, params.owner); + } + + await Task.forCommand('yarn install', { + cwd: targetDir, + optional: true, + }); + await Task.forCommand('yarn lint --fix', { + cwd: targetDir, + optional: true, + }); + + Task.log(); + Task.log(`🎉 Successfully created ${template.id}`); + Task.log(); + } catch (error) { + assertError(error); + Task.error(error.message); + + if (modified) { + Task.log('It seems that something went wrong in the creation process 🤔'); + Task.log(); + Task.log( + 'We have left the changes that were made intact in case you want to', + ); + Task.log( + 'continue manually, but you can also revert the changes and try again.', + ); + + Task.error(`🔥 Failed to create ${template.id}!`); + } + } finally { + tmpDirManager.cleanup(); + } +} diff --git a/packages/cli/src/lib/new/testUtils.ts b/packages/cli/src/lib/new/execution/testUtils.ts similarity index 98% rename from packages/cli/src/lib/new/testUtils.ts rename to packages/cli/src/lib/new/execution/testUtils.ts index 55469d2f0d..e9809d5fd1 100644 --- a/packages/cli/src/lib/new/testUtils.ts +++ b/packages/cli/src/lib/new/execution/testUtils.ts @@ -18,7 +18,7 @@ import { WriteStream } from 'tty'; import { resolve as resolvePath } from 'path'; -import { paths } from '../paths'; +import { paths } from '../../paths'; export function mockPaths(options: { ownDir?: string; diff --git a/packages/cli/src/lib/new/utils.test.ts b/packages/cli/src/lib/new/execution/utils.test.ts similarity index 98% rename from packages/cli/src/lib/new/utils.test.ts rename to packages/cli/src/lib/new/execution/utils.test.ts index 5e15812077..a6fc7abdb5 100644 --- a/packages/cli/src/lib/new/utils.test.ts +++ b/packages/cli/src/lib/new/execution/utils.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { resolvePackageName, createDirName, Options } from './utils'; -import { Template } from './types'; +import { Template } from '../types'; describe('resolvePackageName', () => { it('should generate correct name without scope', () => { diff --git a/packages/cli/src/lib/new/utils.ts b/packages/cli/src/lib/new/execution/utils.ts similarity index 98% rename from packages/cli/src/lib/new/utils.ts rename to packages/cli/src/lib/new/execution/utils.ts index 436151405d..c9c1e08be8 100644 --- a/packages/cli/src/lib/new/utils.ts +++ b/packages/cli/src/lib/new/execution/utils.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Template } from './types'; +import { Template } from '../types'; export interface Options extends Record { id: string;