From 4626abff6b5e4a49261a542111d1f954649cff1c Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sun, 10 Nov 2024 12:26:28 -0500 Subject: [PATCH 001/238] docs: add meta guidelines for new guides Signed-off-by: aramissennyeydd --- docs/plugins/guides/101/meta.md | 33 +++++++++++++++++++++ docs/plugins/{guides.md => guides/index.md} | 0 docs/plugins/guides/meta.md | 18 +++++++++++ 3 files changed, 51 insertions(+) create mode 100644 docs/plugins/guides/101/meta.md rename docs/plugins/{guides.md => guides/index.md} (100%) create mode 100644 docs/plugins/guides/meta.md diff --git a/docs/plugins/guides/101/meta.md b/docs/plugins/guides/101/meta.md new file mode 100644 index 0000000000..fecd43aa0f --- /dev/null +++ b/docs/plugins/guides/101/meta.md @@ -0,0 +1,33 @@ + + +# Intro to Plugin Development + +## Writing Guidelines + +The goal of this section is to take the reader from zero-to-situated. Docs should assume that the user has _only_ gone through the initial getting started docs. At the end of this guide, the user will understand the basics of plugin development. + +### Why build plugins? + +This section should answer definitely why you should build a new plugin instead of adding code to existing packages or using an NPM package. The Backstage framework is deeply empowered by plugins and plugins are core to the project's success. Users should walk away from reading this section with a conviction that plugins are the right path for new functionality. + +### Creating a plugin + +This section should be extremely deliberate in showing readers every step of the way to create a plugin. A reader that finishes this section should feel extremely comfortable adding and installing new plugins regardless of their experience with JS/TS. + +- Setting up your environment +- Scaffolding a new plugin +- Installation syntax +- Core services +- Debugging + - Declaration error, `export default` missing + - Startup error, `httpRouter` failed to start + +### Sustainable plugin development + +Plugins are not developed in a vacuum. Users should reach for them to solve specific business problems facing their developers, for example, you may be tasked to create + +- a new vendor integration like PagerDuty, +- a new plugin backend that talks to an internal service, +- etc. + +This section should contain learnings from successful Backstage deployments about how to engage with stakeholders, how/when to iterate on your plugin, and setting yourself up for future success. diff --git a/docs/plugins/guides.md b/docs/plugins/guides/index.md similarity index 100% rename from docs/plugins/guides.md rename to docs/plugins/guides/index.md diff --git a/docs/plugins/guides/meta.md b/docs/plugins/guides/meta.md new file mode 100644 index 0000000000..f5b053af4b --- /dev/null +++ b/docs/plugins/guides/meta.md @@ -0,0 +1,18 @@ + + +## Glossary + +- Page: A single `md` file. +- Guide: A number of pages grouped under the same folder. +- Progression: Guides are ordered and moving along that order will give you the next guide, for example, "201" will follow "101" or "experienced" will follow "beginner". +- Progression level: Explicit guides in a progression, for example, "101" or "beginner" + +## Writing Guidelines + +The goal of these docs is to provide a comprehensive set of guides that developers + admins can use to initially get quickly up to speed with plugin development, and then refer to as they're developing their own plugins. + +A user that finishes all of these guides will feel comfortable implementing plugins on their own for 90% of cases. For the remaining 10%, they will know where to find help (Discord, Github, source code). The user will also understand why/when to build their own plugins, inner-sourcing their developer portal and contributing internal plugins back to the open-source project. + +At the same time, not all users will finish the docs or they may come back to them as required. Individual guides should have strong "abstracts" (what will I learn by reading this guide), table of contents, and "next steps" (what do I need to do next) to guide users to read the most important pieces for their work. + +When writing guide pages, keep it light! These should be instructional docs, and at the same time conversational and a joy to read. Guides should build on each other, when reading through a progression, the reader should feel more comfortable and confident with concepts as they pop up across progression levels. Guides should be standalone, when finishing one level (for example 101), you should be able to immediately jump into the next (201) without additional research or background. Referencing previous progression levels is ok. From 86549efd177080aa174a441895e309d8c0f089ba Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:13:45 -0500 Subject: [PATCH 002/238] Update docs/plugins/guides/101/meta.md Co-authored-by: Peter Macdonald Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> --- docs/plugins/guides/101/meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/guides/101/meta.md b/docs/plugins/guides/101/meta.md index fecd43aa0f..037a31a228 100644 --- a/docs/plugins/guides/101/meta.md +++ b/docs/plugins/guides/101/meta.md @@ -12,7 +12,7 @@ This section should answer definitely why you should build a new plugin instead ### Creating a plugin -This section should be extremely deliberate in showing readers every step of the way to create a plugin. A reader that finishes this section should feel extremely comfortable adding and installing new plugins regardless of their experience with JS/TS. +This section should be extremely deliberate in showing readers every step of the way to create a plugin using Backstage's best practises. A reader that finishes this section should feel extremely comfortable adding and installing new plugins regardless of their experience with JS/TS and Backstage. - Setting up your environment - Scaffolding a new plugin From 52eb943d653065061301c06d95ba91323451e1a3 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:14:07 -0500 Subject: [PATCH 003/238] Apply suggestions from code review Co-authored-by: Peter Macdonald Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> --- docs/plugins/guides/meta.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/guides/meta.md b/docs/plugins/guides/meta.md index f5b053af4b..0df4c79f9b 100644 --- a/docs/plugins/guides/meta.md +++ b/docs/plugins/guides/meta.md @@ -9,9 +9,9 @@ ## Writing Guidelines -The goal of these docs is to provide a comprehensive set of guides that developers + admins can use to initially get quickly up to speed with plugin development, and then refer to as they're developing their own plugins. +The goal of these docs is to provide a comprehensive set of guides that developers + admins can use to quickly get up to speed with plugin development, and then refer to as they're developing their own plugins. -A user that finishes all of these guides will feel comfortable implementing plugins on their own for 90% of cases. For the remaining 10%, they will know where to find help (Discord, Github, source code). The user will also understand why/when to build their own plugins, inner-sourcing their developer portal and contributing internal plugins back to the open-source project. +A user that finishes all of these guides will feel comfortable implementing plugins on their own. If additional assistance is required, they should be referred to other sources of information such as Discord, GitHub, source code, or documentation for further support. The user will also understand why/when to build their own plugins, inner-sourcing their developer portal and contributing internal plugins back to the open-source project. At the same time, not all users will finish the docs or they may come back to them as required. Individual guides should have strong "abstracts" (what will I learn by reading this guide), table of contents, and "next steps" (what do I need to do next) to guide users to read the most important pieces for their work. From ea7779430a760b6e828968585f55f253bbf82909 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Fri, 29 Nov 2024 22:22:31 -0500 Subject: [PATCH 004/238] remove confusing section around plugin building Signed-off-by: aramissennyeydd --- docs/plugins/guides/101/meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/guides/101/meta.md b/docs/plugins/guides/101/meta.md index 037a31a228..ae51d05eb9 100644 --- a/docs/plugins/guides/101/meta.md +++ b/docs/plugins/guides/101/meta.md @@ -8,7 +8,7 @@ The goal of this section is to take the reader from zero-to-situated. Docs shoul ### Why build plugins? -This section should answer definitely why you should build a new plugin instead of adding code to existing packages or using an NPM package. The Backstage framework is deeply empowered by plugins and plugins are core to the project's success. Users should walk away from reading this section with a conviction that plugins are the right path for new functionality. +This section should answer definitely why you should build a new plugin. The Backstage framework is deeply empowered by plugins and plugins are core to the project's success. Users should walk away from reading this section with a conviction that plugins are the right path for new functionality. ### Creating a plugin From 9940bdca73f2cd0a4e47576a960b6d2a8abc0e1b Mon Sep 17 00:00:00 2001 From: Min Kim Date: Thu, 19 Sep 2024 20:13:44 -0400 Subject: [PATCH 005/238] Create yaml files for existing templates Signed-off-by: Min Kim --- .../templates/alpha/all-default-templates.ts | 38 +++++++++++++++++++ .../alpha/default-backend-module.yaml | 8 ++++ .../alpha/default-backend-plugin.yaml | 8 ++++ .../alpha/default-common-plugin-package.yaml | 8 ++++ .../alpha/default-node-plugin-package.yaml | 8 ++++ .../cli/templates/alpha/default-plugin.yaml | 8 ++++ .../alpha/default-react-plugin-package.yaml | 8 ++++ .../templates/alpha/node-library-package.yaml | 8 ++++ .../templates/alpha/scaffolder-module.yaml | 8 ++++ .../templates/alpha/web-library-package.yaml | 8 ++++ 10 files changed, 110 insertions(+) create mode 100644 packages/cli/templates/alpha/all-default-templates.ts create mode 100644 packages/cli/templates/alpha/default-backend-module.yaml create mode 100644 packages/cli/templates/alpha/default-backend-plugin.yaml create mode 100644 packages/cli/templates/alpha/default-common-plugin-package.yaml create mode 100644 packages/cli/templates/alpha/default-node-plugin-package.yaml create mode 100644 packages/cli/templates/alpha/default-plugin.yaml create mode 100644 packages/cli/templates/alpha/default-react-plugin-package.yaml create mode 100644 packages/cli/templates/alpha/node-library-package.yaml create mode 100644 packages/cli/templates/alpha/scaffolder-module.yaml create mode 100644 packages/cli/templates/alpha/web-library-package.yaml diff --git a/packages/cli/templates/alpha/all-default-templates.ts b/packages/cli/templates/alpha/all-default-templates.ts new file mode 100644 index 0000000000..0a342ea3ea --- /dev/null +++ b/packages/cli/templates/alpha/all-default-templates.ts @@ -0,0 +1,38 @@ +export default [ + { + id: 'backend-module', + target: '@backstage/cli/templates/alpha/default-backend-module.yaml', + }, + { + id: 'backend-plugin', + target: '@backstage/cli/templates/alpha/default-backend-plugin.yaml', + }, + { + id: 'plugin-common', + target: '@backstage/cli/templates/alpha/default-common-plugin-package.yaml', + }, + { + id: 'plugin-node', + target: '@backstage/cli/templates/alpha/default-node-plugin-package.yaml', + }, + { + id: 'frontend-plugion', // changed from 'plugin' + target: '@backstage/cli/templates/alpha/default-plugin.yaml', + }, + { + id: 'plugin-react', + target: '@backstage/cli/templates/alpha/default-react-plugin-package.yaml', + }, + { + id: 'node-library', + target: '@backstage/cli/templates/alpha/node-library-package.yaml', + }, + { + id: 'scaffolder-module', + target: '@backstage/cli/templates/alpha/scaffolder-module.yaml', + }, + { + id: 'web-library', + target: '@backstage/cli/templates/alpha/web-library-package.yaml', + }, +]; diff --git a/packages/cli/templates/alpha/default-backend-module.yaml b/packages/cli/templates/alpha/default-backend-module.yaml new file mode 100644 index 0000000000..54906866e0 --- /dev/null +++ b/packages/cli/templates/alpha/default-backend-module.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/backendModule.ts +description: A new backend module that extends an existing backend plugin with additional features +template: ../default-backend-module +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/default-backend-plugin.yaml b/packages/cli/templates/alpha/default-backend-plugin.yaml new file mode 100644 index 0000000000..e9484fed39 --- /dev/null +++ b/packages/cli/templates/alpha/default-backend-plugin.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/backendPlugin.ts +description: A new backend plugin +template: ../default-backend-plugin +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/default-common-plugin-package.yaml b/packages/cli/templates/alpha/default-common-plugin-package.yaml new file mode 100644 index 0000000000..0f94432e2a --- /dev/null +++ b/packages/cli/templates/alpha/default-common-plugin-package.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/pluginCommon.ts +description: A new isomorphic common plugin package +template: ../default-common-plugin-package +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/default-node-plugin-package.yaml b/packages/cli/templates/alpha/default-node-plugin-package.yaml new file mode 100644 index 0000000000..a461cac8a6 --- /dev/null +++ b/packages/cli/templates/alpha/default-node-plugin-package.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/pluginNode.ts +description: A new Node.js library plugin package +template: ../default-node-plugin-package +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/default-plugin.yaml b/packages/cli/templates/alpha/default-plugin.yaml new file mode 100644 index 0000000000..ac42b393cb --- /dev/null +++ b/packages/cli/templates/alpha/default-plugin.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/frontendPlugin.ts +description: A new frontend plugin +template: ../default-plugin +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/default-react-plugin-package.yaml b/packages/cli/templates/alpha/default-react-plugin-package.yaml new file mode 100644 index 0000000000..f022d990f5 --- /dev/null +++ b/packages/cli/templates/alpha/default-react-plugin-package.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/pluginWeb.ts +description: A new web library plugin package +template: ../default-react-plugin-package +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/node-library-package.yaml b/packages/cli/templates/alpha/node-library-package.yaml new file mode 100644 index 0000000000..51146363a0 --- /dev/null +++ b/packages/cli/templates/alpha/node-library-package.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/nodeLibraryPackage.ts +description: A new node-library package, exporting shared functionality for backend plugins and modules +template: ../node-library-package +targetPath: packages +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/scaffolder-module.yaml b/packages/cli/templates/alpha/scaffolder-module.yaml new file mode 100644 index 0000000000..59fd2e9e3f --- /dev/null +++ b/packages/cli/templates/alpha/scaffolder-module.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/scaffolderModule.ts +description: A module exporting custom actions for @backstage/plugin-scaffolder-backend +template: ../scaffolder-module +targetPath: plugins +prompts: + - id: test + prompt: Test prompt + default: test-default diff --git a/packages/cli/templates/alpha/web-library-package.yaml b/packages/cli/templates/alpha/web-library-package.yaml new file mode 100644 index 0000000000..8580e6ea65 --- /dev/null +++ b/packages/cli/templates/alpha/web-library-package.yaml @@ -0,0 +1,8 @@ +# lib/new/factories/webLibraryPackage.ts +description: A new web-library package, exporting shared functionality for frontend plugins +template: ../web-library-package +targetPath: packages +prompts: + - id: test + prompt: Test prompt + default: test-default From fb8c558f17440ab65a678656bea842c72357e0e9 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Thu, 26 Sep 2024 09:33:45 -0400 Subject: [PATCH 006/238] Add cli config in root pkg.json Signed-off-by: Min Kim --- package.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/package.json b/package.json index 11b419d3fb..8653f76f68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,20 @@ { "name": "root", "version": "1.36.0-next.0", + "backstage": { + "cli": { + "defaults": false, + "globals": { + "private": false + }, + "templates": [ + { + "id": "local web plugin", + "target": "./packages/cli/templates/alpha/web-library-package.yaml" + } + ] + } + }, "private": true, "repository": { "type": "git", From 03c9b8744bb90f2cd78e0f63f717cb46cbe9bd3f Mon Sep 17 00:00:00 2001 From: Min Kim Date: Thu, 26 Sep 2024 09:35:39 -0400 Subject: [PATCH 007/238] Get templates and globals from pkg.json Signed-off-by: Min Kim --- packages/cli/src/commands/new/new.ts | 194 +++++++++++++++------------ 1 file changed, 105 insertions(+), 89 deletions(-) diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts index 2733b0be9b..7dda08626b 100644 --- a/packages/cli/src/commands/new/new.ts +++ b/packages/cli/src/commands/new/new.ts @@ -23,98 +23,114 @@ import { isMonoRepo } from '@backstage/cli-node'; import { paths } from '../../lib/paths'; import { assertError } from '@backstage/errors'; import { Task } from '../../lib/tasks'; +import defaultTemplates from '../../../templates/alpha/all-default-templates'; -function parseOptions(optionStrings: string[]): Record { - const options: Record = {}; - - for (const str of optionStrings) { - const [key] = str.split('=', 1); - const value = str.slice(key.length + 1); - if (!key || str[key.length] !== '=') { - throw new Error( - `Invalid option '${str}', must be of the format =`, - ); - } - options[key] = value; - } - - return options; +interface TemplateLocation { + id: string; + target: string; } -export default async (opts: OptionValues) => { - const factory = await FactoryRegistry.interactiveSelect(opts.select); - - const providedOptions = parseOptions(opts.option); - const options = await FactoryRegistry.populateOptions( - factory, - providedOptions, - ); - - let defaultVersion = '0.1.0'; - if (opts.baseVersion) { - defaultVersion = opts.baseVersion; - } else { - const lernaVersion = await fs - .readJson(paths.resolveTargetRoot('lerna.json')) - .then(pkg => pkg.version) - .catch(() => undefined); - if (lernaVersion) { - defaultVersion = lernaVersion; - } - } - - const tempDirs = new Array(); - async function createTemporaryDirectory(name: string): Promise { - const dir = await fs.mkdtemp(joinPath(os.tmpdir(), name)); - tempDirs.push(dir); - return dir; - } - - const license = opts.license ?? 'Apache-2.0'; - - let modified = false; - try { - await factory.create(options, { - isMonoRepo: await isMonoRepo(), - defaultVersion, - license, - scope: opts.scope?.replace(/^@/, ''), - npmRegistry: opts.npmRegistry, - private: Boolean(opts.private), - createTemporaryDirectory, - markAsModified() { - modified = true; - }, - }); - - Task.log(); - Task.log(`🎉 Successfully created ${factory.name}`); - 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 ${factory.name}!`); - } - } finally { - for (const dir of tempDirs) { - try { - await fs.remove(dir); - } catch (error) { - console.error( - `Failed to remove temporary directory '${dir}', ${error}`, - ); +async function readCliConfig( + cliConfig: + | { + defaults?: boolean; + templates?: TemplateLocation[]; + globals?: Record; } - } + | undefined, +) { + let templates: TemplateLocation[] = []; + const cliTemplates = cliConfig?.templates; + + if (!cliConfig || cliConfig?.defaults) { + templates = defaultTemplates; } + if (cliTemplates?.length) { + cliTemplates.forEach((template: TemplateLocation) => { + templates.push({ + id: template.id, + target: template.target, + }); + }); + } + return { + templates, + globals: { ...cliConfig?.globals }, + }; +} + +export default async () => { + const pkgJson = await fs.readJson(paths.resolveTargetRoot('package.json')); + const cliConfig = pkgJson.backstage?.cli; + + const { templates, globals } = await readCliConfig(cliConfig); + console.log(templates, globals); + + // let defaultVersion = '0.1.0'; + // if (opts.baseVersion) { + // defaultVersion = opts.baseVersion; + // } else { + // const lernaVersion = await fs + // .readJson(paths.resolveTargetRoot('lerna.json')) + // .then(pkg => pkg.version) + // .catch(() => undefined); + // if (lernaVersion) { + // defaultVersion = lernaVersion; + // } + // } + + // const tempDirs = new Array(); + // async function createTemporaryDirectory(name: string): Promise { + // const dir = await fs.mkdtemp(joinPath(os.tmpdir(), name)); + // tempDirs.push(dir); + // return dir; + // } + + // const license = opts.license ?? 'Apache-2.0'; + + // let modified = false; + // try { + // await factory.create(options, { + // isMonoRepo: await isMonoRepo(), + // defaultVersion, + // license, + // scope: opts.scope?.replace(/^@/, ''), + // npmRegistry: opts.npmRegistry, + // private: Boolean(opts.private), + // createTemporaryDirectory, + // markAsModified() { + // modified = true; + // }, + // }); + + // Task.log(); + // Task.log(`🎉 Successfully created ${factory.name}`); + // 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 ${factory.name}!`); + // } + // } finally { + // for (const dir of tempDirs) { + // try { + // await fs.remove(dir); + // } catch (error) { + // console.error( + // `Failed to remove temporary directory '${dir}', ${error}`, + // ); + // } + // } + // } }; From fddab213118d1b669943826a9efcce904b47c131 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Thu, 26 Sep 2024 09:48:02 -0400 Subject: [PATCH 008/238] Interactively select a template from dynamic list Signed-off-by: Min Kim --- packages/cli/src/commands/new/new.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts index 7dda08626b..6eb027aed2 100644 --- a/packages/cli/src/commands/new/new.ts +++ b/packages/cli/src/commands/new/new.ts @@ -18,6 +18,7 @@ import os from 'os'; import fs from 'fs-extra'; import { join as joinPath } from 'path'; import { OptionValues } from 'commander'; +import inquirer from 'inquirer'; import { FactoryRegistry } from '../../lib/new/FactoryRegistry'; import { isMonoRepo } from '@backstage/cli-node'; import { paths } from '../../lib/paths'; @@ -59,12 +60,32 @@ async function readCliConfig( }; } +async function templateSelector( + templates: TemplateLocation[], +): Promise { + const answer = await inquirer.prompt<{ name: TemplateLocation }>([ + { + type: 'list', + name: 'name', + message: 'What do you want to create?', + choices: templates.map(template => { + return { + name: template.id, + value: template, + }; + }), + }, + ]); + return answer.name; +} + export default async () => { const pkgJson = await fs.readJson(paths.resolveTargetRoot('package.json')); const cliConfig = pkgJson.backstage?.cli; const { templates, globals } = await readCliConfig(cliConfig); - console.log(templates, globals); + const template = await templateSelector(templates); + console.log(template, globals); // let defaultVersion = '0.1.0'; // if (opts.baseVersion) { From ce4dcd91aeacbd9cd770fb3454efe80a777b8d08 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Thu, 26 Sep 2024 11:40:05 -0400 Subject: [PATCH 009/238] Verify that a template's skeleton path exists Signed-off-by: Min Kim --- packages/cli/src/commands/new/new.ts | 38 ++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts index 6eb027aed2..d683aeaa6b 100644 --- a/packages/cli/src/commands/new/new.ts +++ b/packages/cli/src/commands/new/new.ts @@ -16,9 +16,10 @@ import os from 'os'; import fs from 'fs-extra'; -import { join as joinPath } from 'path'; +import { join as joinPath, dirname } from 'path'; import { OptionValues } from 'commander'; import inquirer from 'inquirer'; +import { parse } from 'yaml'; import { FactoryRegistry } from '../../lib/new/FactoryRegistry'; import { isMonoRepo } from '@backstage/cli-node'; import { paths } from '../../lib/paths'; @@ -26,6 +27,22 @@ import { assertError } from '@backstage/errors'; import { Task } from '../../lib/tasks'; import defaultTemplates from '../../../templates/alpha/all-default-templates'; +type ConfigurablePrompt = + | { + id: string; + prompt: string; + default?: string | boolean; + } + | string; + +interface Template { + description?: string; + template: string; + targetPath: string; + prompts?: ConfigurablePrompt[]; + additionalActions?: string[]; +} + interface TemplateLocation { id: string; target: string; @@ -79,12 +96,29 @@ async function templateSelector( return answer.name; } +async function verifyTemplate({ target }: TemplateLocation): Promise