cli/new: refactor config reading
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
+2
-4
@@ -7,13 +7,11 @@
|
||||
"url": "https://github.com/backstage/backstage"
|
||||
},
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"defaults": true,
|
||||
"new": {
|
||||
"globals": {
|
||||
"scope": "backstage",
|
||||
"private": false
|
||||
},
|
||||
"templates": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
|
||||
@@ -160,7 +160,8 @@
|
||||
"yargs": "^16.2.0",
|
||||
"yml-loader": "^2.1.0",
|
||||
"yn": "^4.0.0",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^3.22.4",
|
||||
"zod-validation-error": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 fs from 'fs-extra';
|
||||
import { paths } from '../../paths';
|
||||
import { defaultTemplates } from '../defaultTemplates';
|
||||
import { NewConfig } from './types';
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
import { ForwardedError } from '@backstage/errors';
|
||||
|
||||
const pkgJsonWithNewConfigSchema = z.object({
|
||||
backstage: z
|
||||
.object({
|
||||
new: z
|
||||
.object({
|
||||
templates: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
id: z.string(),
|
||||
target: z.string(),
|
||||
})
|
||||
.strict(),
|
||||
)
|
||||
.optional(),
|
||||
globals: z
|
||||
.record(z.union([z.string(), z.number(), z.boolean()]))
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export async function loadNewConfig(): Promise<NewConfig> {
|
||||
const pkgPath = paths.resolveTargetRoot('package.json');
|
||||
const pkgJson = await fs.readJson(pkgPath);
|
||||
|
||||
const parsed = pkgJsonWithNewConfigSchema.safeParse(pkgJson);
|
||||
if (!parsed.success) {
|
||||
throw new ForwardedError(
|
||||
`Failed to load templating configuration from '${pkgPath}'`,
|
||||
fromZodError(parsed.error),
|
||||
);
|
||||
}
|
||||
|
||||
const newConfig = parsed.data.backstage?.new;
|
||||
|
||||
return {
|
||||
isUsingDefaultTemplates: !newConfig?.templates,
|
||||
templatePointers: newConfig?.templates ?? defaultTemplates,
|
||||
globals: newConfig?.globals ?? {},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type NewConfig = {
|
||||
/**
|
||||
* The pointers to templates that can be used.
|
||||
*/
|
||||
templatePointers: NewTemplatePointer[];
|
||||
|
||||
/**
|
||||
* Whether the default set of templates are being used or not.
|
||||
*/
|
||||
isUsingDefaultTemplates: boolean;
|
||||
|
||||
/**
|
||||
* Templating globals that should apply to all templates.
|
||||
*/
|
||||
globals: {
|
||||
[KName in string]?: number | string | boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type NewTemplatePointer = {
|
||||
id: string;
|
||||
target: string;
|
||||
};
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import { isMonoRepo } from '@backstage/cli-node';
|
||||
@@ -24,16 +23,13 @@ import { paths } from '../paths';
|
||||
import { Task } from '../tasks';
|
||||
import { addCodeownersEntry, getCodeownersFilePath } from '../codeowners';
|
||||
|
||||
import {
|
||||
readCliConfig,
|
||||
templateSelector,
|
||||
verifyTemplate,
|
||||
} from './templateSelector';
|
||||
import { templateSelector, verifyTemplate } from './templateSelector';
|
||||
import { promptOptions } from './prompts';
|
||||
import { populateOptions, createDirName, resolvePackageName } from './utils';
|
||||
import { runAdditionalActions } from './additionalActions';
|
||||
import { executePluginPackageTemplate } from './executeTemplate';
|
||||
import { TemporaryDirectoryManager } from './TemporaryDirectoryManager';
|
||||
import { loadNewConfig } from './config/loadNewConfig';
|
||||
|
||||
export type CreateNewPackageOptions = {
|
||||
preselectedTemplateId?: string;
|
||||
@@ -48,12 +44,13 @@ export type CreateNewPackageOptions = {
|
||||
};
|
||||
|
||||
export async function createNewPackage(options: CreateNewPackageOptions) {
|
||||
const pkgJson = await fs.readJson(paths.resolveTargetRoot('package.json'));
|
||||
const cliConfig = pkgJson.backstage?.cli;
|
||||
const newConfig = await loadNewConfig();
|
||||
|
||||
const { templates, globals } = readCliConfig(cliConfig);
|
||||
const template = verifyTemplate(
|
||||
await templateSelector(templates, options.preselectedTemplateId),
|
||||
await templateSelector(
|
||||
newConfig.templatePointers,
|
||||
options.preselectedTemplateId,
|
||||
),
|
||||
);
|
||||
|
||||
const codeOwnersFilePath = await getCodeownersFilePath(paths.targetRoot);
|
||||
@@ -74,7 +71,7 @@ export async function createNewPackage(options: CreateNewPackageOptions) {
|
||||
typeof prompt === 'string' ? prompt : prompt.id,
|
||||
),
|
||||
) ?? [],
|
||||
globals,
|
||||
globals: newConfig.globals,
|
||||
codeOwnersFilePath,
|
||||
});
|
||||
const answers = { ...prefilledAnswers, ...promptAnswers };
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const defaultTemplates = [
|
||||
{
|
||||
id: 'backend-module',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-backend-module.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'backend-plugin',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-backend-plugin.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'plugin-common',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-common-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'plugin-node',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-node-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'frontend-plugin',
|
||||
target: require.resolve('@backstage/cli/templates/default-plugin.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'plugin-react',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-react-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'node-library',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/node-library-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'scaffolder-module',
|
||||
target: require.resolve('@backstage/cli/templates/scaffolder-module.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'web-library',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/web-library-package.yaml',
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -95,7 +95,7 @@ export async function promptOptions({
|
||||
codeOwnersFilePath,
|
||||
}: {
|
||||
prompts: ConfigurablePrompt[];
|
||||
globals: Record<string, string>;
|
||||
globals: { [name in string]?: string | boolean | number };
|
||||
codeOwnersFilePath: string | undefined;
|
||||
}): Promise<Record<string, string>> {
|
||||
const answers = await inquirer.prompt(
|
||||
|
||||
@@ -19,31 +19,9 @@ import { parse } from 'yaml';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { paths } from '../paths';
|
||||
import { Template, TemplateLocation, CliConfig } from './types';
|
||||
import { Template, TemplateLocation } from './types';
|
||||
|
||||
import defaultTemplates from '../../../templates/all-default-templates';
|
||||
|
||||
export function readCliConfig(cliConfig: CliConfig) {
|
||||
let templates: TemplateLocation[] = [];
|
||||
|
||||
if (!cliConfig || cliConfig?.defaults) {
|
||||
templates = defaultTemplates;
|
||||
}
|
||||
|
||||
const cliTemplates = cliConfig?.templates;
|
||||
if (cliTemplates?.length) {
|
||||
cliTemplates.forEach((template: TemplateLocation) => {
|
||||
templates.push({
|
||||
id: template.id,
|
||||
target: template.target,
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
templates,
|
||||
globals: { ...cliConfig?.globals },
|
||||
};
|
||||
}
|
||||
import { defaultTemplates } from './defaultTemplates';
|
||||
|
||||
export async function templateSelector(
|
||||
templates: TemplateLocation[],
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { paths } from '../src/lib/paths';
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'backend-module',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-backend-module.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'backend-plugin',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-backend-plugin.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'plugin-common',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-common-plugin-package.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'plugin-node',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-node-plugin-package.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'frontend-plugin', // changed from 'plugin'
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-plugin.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'plugin-react',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/default-react-plugin-package.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'node-library',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/node-library-package.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'scaffolder-module',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/scaffolder-module.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'web-library',
|
||||
target: paths.resolveTargetRoot('node_modules/@backstage/cli/templates/web-library-package.yaml'),
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user