diff --git a/package.json b/package.json index c64b7f6951..2622f1e322 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "docgen": "lerna run docgen", "docker-build:app": "yarn workspace example-app build && docker build . -t spotify/backstage", "docker-build": "yarn tsc && yarn workspace example-backend build-image", - "create-plugin": "backstage-cli create-plugin", + "create-plugin": "backstage-cli create-plugin --scope backstage --no-private", "remove-plugin": "backstage-cli remove-plugin", "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push --force-publish; fi", "prettier:check": "prettier --check .", diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 5e6882a4a0..247812b6fd 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -21,6 +21,7 @@ import inquirer, { Answers, Question } from 'inquirer'; import { exec as execCb } from 'child_process'; import { resolve as resolvePath } from 'path'; import os from 'os'; +import { Command } from 'commander'; import { parseOwnerIds, addCodeownersEntry, @@ -32,12 +33,12 @@ import { version as backstageVersion } from '../../lib/version'; const exec = promisify(execCb); -async function checkExists(rootDir: string, id: string) { - await Task.forItem('checking', id, async () => { - const destination = resolvePath(rootDir, 'plugins', id); - +async function checkExists(destination: string) { + await Task.forItem('checking', destination, async () => { if (await fs.pathExists(destination)) { - const existing = chalk.cyan(destination.replace(`${rootDir}/`, '')); + const existing = chalk.cyan( + destination.replace(`${paths.targetRoot}/`, ''), + ); throw new Error( `A plugin with the same name already exists: ${existing}\nPlease try again with a different plugin ID`, ); @@ -86,10 +87,9 @@ export const addExportStatement = async ( export async function addPluginDependencyToApp( rootDir: string, - pluginName: string, + pluginPackage: string, versionStr: string, ) { - const pluginPackage = `@backstage/plugin-${pluginName}`; const packageFilePath = 'packages/app/package.json'; const packageFile = resolvePath(rootDir, packageFilePath); @@ -116,8 +116,11 @@ export async function addPluginDependencyToApp( }); } -export async function addPluginToApp(rootDir: string, pluginName: string) { - const pluginPackage = `@backstage/plugin-${pluginName}`; +export async function addPluginToApp( + rootDir: string, + pluginName: string, + pluginPackage: string, +) { const pluginNameCapitalized = pluginName .split('-') .map(name => capitalize(name)) @@ -175,7 +178,7 @@ export async function movePlugin( }); } -export default async () => { +export default async (cmd: Command) => { const codeownersPath = await getCodeownersFilePath(paths.targetRoot); const questions: Question[] = [ @@ -221,20 +224,29 @@ export default async () => { } const answers: Answers = await inquirer.prompt(questions); - + const name = cmd.scope + ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` + : `plugin-${answers.id}`; + const npmRegistry = cmd.npmRegistry && cmd.scope ? cmd.npmRegistry : ''; + const privatePackage = cmd.private === false ? false : true; + const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json')); const appPackage = paths.resolveTargetRoot('packages/app'); const templateDir = paths.resolveOwn('templates/default-plugin'); const tempDir = resolvePath(os.tmpdir(), answers.id); - const pluginDir = paths.resolveTargetRoot('plugins', answers.id); + const pluginDir = isMonoRepo + ? paths.resolveTargetRoot('plugins', answers.id) + : paths.resolveTargetRoot(answers.id); const ownerIds = parseOwnerIds(answers.owner); - const { version } = await fs.readJson(paths.resolveTargetRoot('lerna.json')); + const { version } = isMonoRepo + ? await fs.readJson(paths.resolveTargetRoot('lerna.json')) + : { version: '0.1.0' }; Task.log(); Task.log('Creating the plugin...'); try { Task.section('Checking if the plugin ID is available'); - await checkExists(paths.targetRoot, answers.id); + await checkExists(pluginDir); Task.section('Creating a temporary plugin directory'); await createTemporaryPluginFolder(tempDir); @@ -244,6 +256,9 @@ export default async () => { ...answers, version, backstageVersion, + name, + privatePackage, + npmRegistry, }); Task.section('Moving to final location'); @@ -254,10 +269,10 @@ export default async () => { if (await fs.pathExists(appPackage)) { Task.section('Adding plugin as dependency in app'); - await addPluginDependencyToApp(paths.targetRoot, answers.id, version); + await addPluginDependencyToApp(paths.targetRoot, name, version); Task.section('Import plugin in app'); - await addPluginToApp(paths.targetRoot, answers.id); + await addPluginToApp(paths.targetRoot, answers.id, name); } if (ownerIds && ownerIds.length) { @@ -269,11 +284,7 @@ export default async () => { } Task.log(); - Task.log( - `🥇 Successfully created ${chalk.cyan( - `@backstage/plugin-${answers.id}`, - )}`, - ); + Task.log(`🥇 Successfully created ${chalk.cyan(`${name}`)}`); Task.log(); Task.exit(); } catch (error) { diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 4e031cdbc1..f1828cef84 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -62,6 +62,9 @@ export function registerCommands(program: CommanderStatic) { program .command('create-plugin') .description('Creates a new plugin in the current repository') + .option('--scope ', 'NPM scope') + .option('--npm-registry ', 'NPM registry URL') + .option('--no-private', 'Public NPM Package') .action( lazy(() => import('./create-plugin/createPlugin').then(m => m.default)), ); diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts index 99af093f2c..f5dfa670b7 100644 --- a/packages/cli/src/commands/plugin/diff.ts +++ b/packages/cli/src/commands/plugin/diff.ts @@ -30,6 +30,9 @@ import { version as backstageVersion } from '../../lib/version'; export type PluginData = { id: string; name: string; + privatePackage: string; + version: string; + npmRegistry: string; }; const fileHandlers = [ @@ -62,11 +65,8 @@ export default async (cmd: Command) => { promptFunc = yesPromptFunc; } - const { version } = await fs.readJson(paths.resolveTargetRoot('lerna.json')); - const data = await readPluginData(); const templateFiles = await diffTemplateFiles('default-plugin', { - version, backstageVersion, ...data, }); @@ -77,9 +77,19 @@ export default async (cmd: Command) => { // Reads templating data from the existing plugin async function readPluginData(): Promise { let name: string; + let privatePackage: string; + let version: string; + let npmRegistry: string; try { const pkg = require(paths.resolveTarget('package.json')); name = pkg.name; + privatePackage = pkg.private; + version = pkg.version; + const scope = name.split('/')[0]; + if (`${scope}:registry` in pkg.publishConfig) { + const registryURL = pkg.publishConfig[`${scope}:registry`]; + npmRegistry = `"${scope}:registry" : "${registryURL}"`; + } else npmRegistry = ''; } catch (error) { throw new Error(`Failed to read target package, ${error}`); } @@ -96,5 +106,5 @@ async function readPluginData(): Promise { const id = pluginIdMatch[1]; - return { id, name }; + return { id, name, privatePackage, version, npmRegistry }; } diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index dbfb639b16..52cffeb621 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,11 +1,14 @@ { - "name": "@backstage/plugin-{{id}}", + "name": "{{name}}", "version": "{{version}}", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, +{{#if privatePackage}} "private": {{privatePackage}}, +{{/if}} "publishConfig": { +{{#if npmRegistry}} "registry": "{{npmRegistry}}", +{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index b95b5bbc5b..19e3891551 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -16,7 +16,7 @@ "test:all": "lerna run test -- --coverage", "lint": "lerna run lint --since origin/master --", "lint:all": "lerna run lint --", - "create-plugin": "backstage-cli create-plugin", + "create-plugin": "backstage-cli create-plugin --scope backstage --no-private", "remove-plugin": "backstage-cli remove-plugin" }, "workspaces": { diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 146a3ec644..969a77cb2a 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -81,7 +81,11 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { const path = paths.resolveOwnRoot(pkgJsonPath); const pkgTemplate = await fs.readFile(path, 'utf8'); const { dependencies = {}, devDependencies = {} } = JSON.parse( - handlebars.compile(pkgTemplate)({ version: '0.0.0' }), + handlebars.compile(pkgTemplate)({ + version: '0.0.0', + privatePackage: true, + scopeName: '@backstage', + }), ); Array()