cli/new: move template execution impl to subdir
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -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', () => ({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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;
|
||||
+1
-1
@@ -19,7 +19,7 @@ import { mockPaths } from './testUtils';
|
||||
import {
|
||||
executePluginPackageTemplate,
|
||||
templatingTask,
|
||||
} from './executeTemplate';
|
||||
} from './executePluginPackageTemplate';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('executePluginPackageTemplate', () => {
|
||||
+5
-5
@@ -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,
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
+1
-1
@@ -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', () => {
|
||||
@@ -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<string, string | boolean> {
|
||||
id: string;
|
||||
Reference in New Issue
Block a user