From e40f2b1f54cf74815aedb52ef5612e444ae4c068 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Mon, 7 Sep 2020 22:08:12 +0800 Subject: [PATCH 01/21] remove mono repo dependency and private npm registry option --- package.json | 3 +- .../commands/create-plugin/createPlugin.ts | 50 +++++++++++++------ packages/cli/src/commands/index.ts | 3 ++ packages/cli/src/lib/tasks.ts | 7 +++ .../templates/default-plugin/package.json.hbs | 5 +- .../templates/default-plugin/tsconfig.json | 13 +++++ packages/e2e-test/src/e2e-test.ts | 6 ++- 7 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 packages/cli/templates/default-plugin/tsconfig.json diff --git a/package.json b/package.json index 93f8f694a6..1ddfc8bb03 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "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 internal", + "create-oss-plugin": "backstage-cli create-plugin --scope backstage", "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..fe23acc924 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`, ); @@ -88,8 +89,9 @@ export async function addPluginDependencyToApp( rootDir: string, pluginName: string, versionStr: string, + scopeNameWithSlash: string, ) { - const pluginPackage = `@backstage/plugin-${pluginName}`; + const pluginPackage = `${scopeNameWithSlash}plugin-${pluginName}`; const packageFilePath = 'packages/app/package.json'; const packageFile = resolvePath(rootDir, packageFilePath); @@ -116,8 +118,12 @@ 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, + scopeNameWithSlash: string, +) { + const pluginPackage = `${scopeNameWithSlash}plugin-${pluginName}`; const pluginNameCapitalized = pluginName .split('-') .map(name => capitalize(name)) @@ -175,8 +181,12 @@ export async function movePlugin( }); } -export default async () => { +export default async (cmd: Command) => { const codeownersPath = await getCodeownersFilePath(paths.targetRoot); + const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; + const scopeNameWithSlash = cmd.scope ? `${scopeName}/` : ''; + const privatePackage = cmd.private === false ? false : true; + const registryURL = cmd.npmRegistry; const questions: Question[] = [ { @@ -225,16 +235,18 @@ export default async () => { 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 = (await fs.pathExists(paths.resolveTargetRoot('plugins'))) + ? 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 = backstageVersion; 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, + scopeName, + privatePackage, + registryURL, }); Task.section('Moving to final location'); @@ -254,10 +269,15 @@ 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, + answers.id, + version, + scopeNameWithSlash, + ); Task.section('Import plugin in app'); - await addPluginToApp(paths.targetRoot, answers.id); + await addPluginToApp(paths.targetRoot, answers.id, scopeNameWithSlash); } if (ownerIds && ownerIds.length) { @@ -271,7 +291,7 @@ export default async () => { Task.log(); Task.log( `🥇 Successfully created ${chalk.cyan( - `@backstage/plugin-${answers.id}`, + `${scopeNameWithSlash}plugin-${answers.id}`, )}`, ); Task.log(); 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/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 0753301b78..38f2636b81 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -19,6 +19,7 @@ import fs from 'fs-extra'; import handlebars from 'handlebars'; import ora from 'ora'; import { basename, dirname } from 'path'; +import { paths } from './paths'; import recursive from 'recursive-readdir'; const TASK_NAME_MAX_LENGTH = 14; @@ -73,6 +74,10 @@ export async function templatingTask( throw new Error(`Failed to read template directory: ${error.message}`); }); + const isMonoRepo = (await fs.pathExists(paths.resolveTargetRoot('plugins'))) + ? true + : false; + for (const file of files) { const destinationFile = file.replace(templateDir, destinationDir); await fs.ensureDir(dirname(destinationFile)); @@ -92,6 +97,8 @@ export async function templatingTask( }); }); } else { + if (isMonoRepo && basename(file) === 'tsconfig.json') continue; + await Task.forItem('copying', basename(file), async () => { await fs.copyFile(file, destinationFile).catch(error => { const destination = destinationFile; diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 38d4e41b5b..ad217b0be1 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,11 +1,12 @@ { - "name": "@backstage/plugin-{{id}}", + "name": "{{#if scopeName}}{{scopeName}}/{{/if}}/plugin-{{id}}", "version": "{{version}}", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, + "private": {{privatePackage}}, "publishConfig": { + {{#if registryURL}}"{{scopeName}}:registry":"{{registryURL}}",{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/default-plugin/tsconfig.json b/packages/cli/templates/default-plugin/tsconfig.json new file mode 100644 index 0000000000..d36fba9e4d --- /dev/null +++ b/packages/cli/templates/default-plugin/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@backstage/cli/config/tsconfig.json", + "include": [ + "src", + "dev", + "migrations" + ], + "exclude": ["node_modules"], + "compilerOptions": { + "outDir": "dist-types", + "skipLibCheck": true + } +} diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 5dfac68d6a..99ba1426d4 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() From adaa61e9ac43c6dc6beb1d56e8fc3b0ef5f627d1 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Tue, 8 Sep 2020 09:25:10 +0800 Subject: [PATCH 02/21] improvement --- package.json | 3 +-- packages/cli/src/lib/tasks.ts | 4 +--- packages/cli/templates/default-plugin/package.json.hbs | 2 +- packages/cli/templates/default-plugin/tsconfig.json | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 1ddfc8bb03..b25bcd3788 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,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 --scope internal", - "create-oss-plugin": "backstage-cli create-plugin --scope backstage", + "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/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 38f2636b81..0573b8fda8 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -74,9 +74,7 @@ export async function templatingTask( throw new Error(`Failed to read template directory: ${error.message}`); }); - const isMonoRepo = (await fs.pathExists(paths.resolveTargetRoot('plugins'))) - ? true - : false; + const isMonoRepo = paths.resolveTargetRoot('lerna.json'); for (const file of files) { const destinationFile = file.replace(templateDir, destinationDir); diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index ad217b0be1..1180d67ea8 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,5 +1,5 @@ { - "name": "{{#if scopeName}}{{scopeName}}/{{/if}}/plugin-{{id}}", + "name": "{{#if scopeName}}{{scopeName}}/{{/if}}plugin-{{id}}", "version": "{{version}}", "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/cli/templates/default-plugin/tsconfig.json b/packages/cli/templates/default-plugin/tsconfig.json index d36fba9e4d..40e967d267 100644 --- a/packages/cli/templates/default-plugin/tsconfig.json +++ b/packages/cli/templates/default-plugin/tsconfig.json @@ -5,9 +5,8 @@ "dev", "migrations" ], - "exclude": ["node_modules"], "compilerOptions": { "outDir": "dist-types", - "skipLibCheck": true + "rootDir": "." } } From 11cddc25aa6c51af574ca87a99aba4b0cd285d6c Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Thu, 10 Sep 2020 13:14:32 +0800 Subject: [PATCH 03/21] rename packageName --- .../commands/create-plugin/createPlugin.ts | 34 +++++++------------ .../templates/default-plugin/package.json.hbs | 4 +-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index fe23acc924..001a86ba6f 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -87,11 +87,9 @@ export const addExportStatement = async ( export async function addPluginDependencyToApp( rootDir: string, - pluginName: string, versionStr: string, - scopeNameWithSlash: string, + pluginPackage: string, ) { - const pluginPackage = `${scopeNameWithSlash}plugin-${pluginName}`; const packageFilePath = 'packages/app/package.json'; const packageFile = resolvePath(rootDir, packageFilePath); @@ -121,9 +119,8 @@ export async function addPluginDependencyToApp( export async function addPluginToApp( rootDir: string, pluginName: string, - scopeNameWithSlash: string, + pluginPackage: string, ) { - const pluginPackage = `${scopeNameWithSlash}plugin-${pluginName}`; const pluginNameCapitalized = pluginName .split('-') .map(name => capitalize(name)) @@ -183,10 +180,6 @@ export async function movePlugin( export default async (cmd: Command) => { const codeownersPath = await getCodeownersFilePath(paths.targetRoot); - const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; - const scopeNameWithSlash = cmd.scope ? `${scopeName}/` : ''; - const privatePackage = cmd.private === false ? false : true; - const registryURL = cmd.npmRegistry; const questions: Question[] = [ { @@ -231,6 +224,13 @@ export default async (cmd: Command) => { } const answers: Answers = await inquirer.prompt(questions); + const packageName = cmd.scope + ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` + : `plugin-${answers.id}`; + const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; + const registryURL = (cmd.npmRegistry && + cmd.scope)`"${scopeName}:registry": "${cmd.npmRegistry}",`; + const privatePackage = cmd.private === false ? false : true; const appPackage = paths.resolveTargetRoot('packages/app'); const templateDir = paths.resolveOwn('templates/default-plugin'); @@ -257,6 +257,7 @@ export default async (cmd: Command) => { version, backstageVersion, scopeName, + packageName, privatePackage, registryURL, }); @@ -269,15 +270,10 @@ export default async (cmd: Command) => { if (await fs.pathExists(appPackage)) { Task.section('Adding plugin as dependency in app'); - await addPluginDependencyToApp( - paths.targetRoot, - answers.id, - version, - scopeNameWithSlash, - ); + await addPluginDependencyToApp(paths.targetRoot, version, packageName); Task.section('Import plugin in app'); - await addPluginToApp(paths.targetRoot, answers.id, scopeNameWithSlash); + await addPluginToApp(paths.targetRoot, answers.id, packageName); } if (ownerIds && ownerIds.length) { @@ -289,11 +285,7 @@ export default async (cmd: Command) => { } Task.log(); - Task.log( - `🥇 Successfully created ${chalk.cyan( - `${scopeNameWithSlash}plugin-${answers.id}`, - )}`, - ); + Task.log(`🥇 Successfully created ${chalk.cyan(`${packageName}`)}`); Task.log(); Task.exit(); } catch (error) { diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 1180d67ea8..e6f9209997 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,12 +1,12 @@ { - "name": "{{#if scopeName}}{{scopeName}}/{{/if}}plugin-{{id}}", + "name": "{{packageName}}", "version": "{{version}}", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "private": {{privatePackage}}, "publishConfig": { - {{#if registryURL}}"{{scopeName}}:registry":"{{registryURL}}",{{/if}} + {{registryURL}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" From 816f158623a86cac23da8387aebc31cff8e1b3cc Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Thu, 10 Sep 2020 14:02:26 +0800 Subject: [PATCH 04/21] fix registryURL --- packages/cli/src/commands/create-plugin/createPlugin.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 001a86ba6f..561fa36f11 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -228,8 +228,10 @@ export default async (cmd: Command) => { ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` : `plugin-${answers.id}`; const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; - const registryURL = (cmd.npmRegistry && - cmd.scope)`"${scopeName}:registry": "${cmd.npmRegistry}",`; + const registryURL = + cmd.npmRegistry && cmd.scope + ? `"${scopeName}:registry": "${cmd.npmRegistry}",` + : null; const privatePackage = cmd.private === false ? false : true; const appPackage = paths.resolveTargetRoot('packages/app'); From 9cb2cfe2eec407af162b7abc674bf00230c0ddba Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Thu, 10 Sep 2020 17:23:00 +0800 Subject: [PATCH 05/21] detect monorepo --- packages/cli/src/commands/create-plugin/createPlugin.ts | 4 +++- packages/cli/src/lib/tasks.ts | 2 +- packages/create-app/templates/default-app/package.json.hbs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 561fa36f11..48bc9025d9 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -237,9 +237,11 @@ export default async (cmd: Command) => { const appPackage = paths.resolveTargetRoot('packages/app'); const templateDir = paths.resolveOwn('templates/default-plugin'); const tempDir = resolvePath(os.tmpdir(), answers.id); - const pluginDir = (await fs.pathExists(paths.resolveTargetRoot('plugins'))) + const pluginDir = (await fs.pathExists(paths.resolveTargetRoot('lerna.json'))) ? paths.resolveTargetRoot('plugins', answers.id) : paths.resolveTargetRoot(answers.id); + console.log(await paths.resolveTargetRoot('lerna.json')); + console.log(await pluginDir); const ownerIds = parseOwnerIds(answers.owner); const version = backstageVersion; diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 0573b8fda8..34f796165b 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -74,7 +74,7 @@ export async function templatingTask( throw new Error(`Failed to read template directory: ${error.message}`); }); - const isMonoRepo = paths.resolveTargetRoot('lerna.json'); + const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json')); for (const file of files) { const destinationFile = file.replace(templateDir, destinationDir); 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": { From 1db225a02f630ecd3d6ee3a9131247fcfff386b5 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Thu, 10 Sep 2020 18:09:09 +0800 Subject: [PATCH 06/21] keep addPluginDependencyToApp argument consistent --- packages/cli/src/commands/create-plugin/createPlugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 48bc9025d9..398009ca8f 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -87,8 +87,8 @@ export const addExportStatement = async ( export async function addPluginDependencyToApp( rootDir: string, - versionStr: string, pluginPackage: string, + versionStr: string, ) { const packageFilePath = 'packages/app/package.json'; const packageFile = resolvePath(rootDir, packageFilePath); @@ -274,7 +274,7 @@ export default async (cmd: Command) => { if (await fs.pathExists(appPackage)) { Task.section('Adding plugin as dependency in app'); - await addPluginDependencyToApp(paths.targetRoot, version, packageName); + await addPluginDependencyToApp(paths.targetRoot, packageName, version); Task.section('Import plugin in app'); await addPluginToApp(paths.targetRoot, answers.id, packageName); From 7f8f8f75726e0b864e423862d3ca96a92a8d860a Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Mon, 14 Sep 2020 14:40:02 +0800 Subject: [PATCH 07/21] un-escape var --- packages/cli/templates/default-plugin/package.json.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 120563c38a..251521e0c7 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": {{privatePackage}}, "publishConfig": { - {{registryURL}} + {{{registryURL}}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" From f90d645461bf82f5745c262e042671c6b04cb362 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Mon, 14 Sep 2020 15:50:18 +0800 Subject: [PATCH 08/21] fix plugin:diff --- .../cli/src/commands/create-plugin/createPlugin.ts | 10 +++++----- packages/cli/src/commands/plugin/diff.ts | 2 ++ packages/cli/templates/default-plugin/package.json.hbs | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 398009ca8f..f022f4fe9e 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -224,7 +224,7 @@ export default async (cmd: Command) => { } const answers: Answers = await inquirer.prompt(questions); - const packageName = cmd.scope + const name = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` : `plugin-${answers.id}`; const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; @@ -261,7 +261,7 @@ export default async (cmd: Command) => { version, backstageVersion, scopeName, - packageName, + name, privatePackage, registryURL, }); @@ -274,10 +274,10 @@ export default async (cmd: Command) => { if (await fs.pathExists(appPackage)) { Task.section('Adding plugin as dependency in app'); - await addPluginDependencyToApp(paths.targetRoot, packageName, version); + await addPluginDependencyToApp(paths.targetRoot, name, version); Task.section('Import plugin in app'); - await addPluginToApp(paths.targetRoot, answers.id, packageName); + await addPluginToApp(paths.targetRoot, answers.id, name); } if (ownerIds && ownerIds.length) { @@ -289,7 +289,7 @@ export default async (cmd: Command) => { } Task.log(); - Task.log(`🥇 Successfully created ${chalk.cyan(`${packageName}`)}`); + Task.log(`🥇 Successfully created ${chalk.cyan(`${name}`)}`); Task.log(); Task.exit(); } catch (error) { diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts index 99af093f2c..064c72c448 100644 --- a/packages/cli/src/commands/plugin/diff.ts +++ b/packages/cli/src/commands/plugin/diff.ts @@ -65,9 +65,11 @@ export default async (cmd: Command) => { const { version } = await fs.readJson(paths.resolveTargetRoot('lerna.json')); const data = await readPluginData(); + const privatePackage = false; const templateFiles = await diffTemplateFiles('default-plugin', { version, backstageVersion, + privatePackage, ...data, }); await handleAllFiles(fileHandlers, templateFiles, promptFunc); diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 251521e0c7..ed1a56d605 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,5 +1,5 @@ { - "name": "{{packageName}}", + "name": "{{name}}", "version": "{{version}}", "main": "src/index.ts", "types": "src/index.ts", From 6cc089abaebec7b64a74a1966fab3d6b7785c0d4 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Mon, 14 Sep 2020 15:56:52 +0800 Subject: [PATCH 09/21] remove console.log --- packages/cli/src/commands/create-plugin/createPlugin.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index f022f4fe9e..8c2a9eeb98 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -240,8 +240,6 @@ export default async (cmd: Command) => { const pluginDir = (await fs.pathExists(paths.resolveTargetRoot('lerna.json'))) ? paths.resolveTargetRoot('plugins', answers.id) : paths.resolveTargetRoot(answers.id); - console.log(await paths.resolveTargetRoot('lerna.json')); - console.log(await pluginDir); const ownerIds = parseOwnerIds(answers.owner); const version = backstageVersion; From 111fbb9e0fcea84532e63838a9d09232478c357d Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Tue, 15 Sep 2020 13:30:18 +0800 Subject: [PATCH 10/21] fix plugin:diff --- .../commands/create-plugin/createPlugin.ts | 8 ++++---- packages/cli/src/commands/plugin/diff.ts | 20 +++++++++++++------ .../templates/default-plugin/package.json.hbs | 3 ++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 8c2a9eeb98..8241f68b8c 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -228,10 +228,10 @@ export default async (cmd: Command) => { ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` : `plugin-${answers.id}`; const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; - const registryURL = + const npmRegistry = cmd.npmRegistry && cmd.scope - ? `"${scopeName}:registry": "${cmd.npmRegistry}",` - : null; + ? `"${scopeName}:registry": "${cmd.npmRegistry}"` + : ''; const privatePackage = cmd.private === false ? false : true; const appPackage = paths.resolveTargetRoot('packages/app'); @@ -261,7 +261,7 @@ export default async (cmd: Command) => { scopeName, name, privatePackage, - registryURL, + npmRegistry, }); Task.section('Moving to final location'); diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts index 064c72c448..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,14 +65,9 @@ export default async (cmd: Command) => { promptFunc = yesPromptFunc; } - const { version } = await fs.readJson(paths.resolveTargetRoot('lerna.json')); - const data = await readPluginData(); - const privatePackage = false; const templateFiles = await diffTemplateFiles('default-plugin', { - version, backstageVersion, - privatePackage, ...data, }); await handleAllFiles(fileHandlers, templateFiles, promptFunc); @@ -79,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}`); } @@ -98,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 ed1a56d605..1a969f9712 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -6,7 +6,8 @@ "license": "Apache-2.0", "private": {{privatePackage}}, "publishConfig": { - {{{registryURL}}} +{{#if npmRegistry}} {{{npmRegistry}}}, +{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" From 903c9d9c63694a1ba178774b77f05094953a9aee Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Wed, 16 Sep 2020 14:06:14 +0800 Subject: [PATCH 11/21] fix plugin template verification --- .../cli/src/commands/create-plugin/createPlugin.ts | 4 ++-- packages/cli/src/lib/tasks.ts | 14 -------------- .../cli/templates/default-plugin/package.json.hbs | 3 ++- .../cli/templates/default-plugin/tsconfig.json | 12 ------------ 4 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 packages/cli/templates/default-plugin/tsconfig.json diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 8241f68b8c..9b97746c0e 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -233,11 +233,11 @@ export default async (cmd: Command) => { ? `"${scopeName}:registry": "${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 = (await fs.pathExists(paths.resolveTargetRoot('lerna.json'))) + const pluginDir = isMonoRepo ? paths.resolveTargetRoot('plugins', answers.id) : paths.resolveTargetRoot(answers.id); const ownerIds = parseOwnerIds(answers.owner); diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 34f796165b..75794a1ba6 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -19,7 +19,6 @@ import fs from 'fs-extra'; import handlebars from 'handlebars'; import ora from 'ora'; import { basename, dirname } from 'path'; -import { paths } from './paths'; import recursive from 'recursive-readdir'; const TASK_NAME_MAX_LENGTH = 14; @@ -74,8 +73,6 @@ export async function templatingTask( throw new Error(`Failed to read template directory: ${error.message}`); }); - const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json')); - for (const file of files) { const destinationFile = file.replace(templateDir, destinationDir); await fs.ensureDir(dirname(destinationFile)); @@ -94,17 +91,6 @@ export async function templatingTask( ); }); }); - } else { - if (isMonoRepo && basename(file) === 'tsconfig.json') continue; - - await Task.forItem('copying', basename(file), async () => { - await fs.copyFile(file, destinationFile).catch(error => { - const destination = destinationFile; - throw new Error( - `Failed to copy file to ${destination} : ${error.message}`, - ); - }); - }); } } } diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 1a969f9712..5e59b6fa0d 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -4,7 +4,8 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": {{privatePackage}}, +{{#if privatePackage}} "private": {{privatePackage}}, +{{/if}} "publishConfig": { {{#if npmRegistry}} {{{npmRegistry}}}, {{/if}} diff --git a/packages/cli/templates/default-plugin/tsconfig.json b/packages/cli/templates/default-plugin/tsconfig.json deleted file mode 100644 index 40e967d267..0000000000 --- a/packages/cli/templates/default-plugin/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - "dev", - "migrations" - ], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} From 21aa93ad46a49b49175a27c6c9bc39dcf3de6231 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Wed, 16 Sep 2020 14:56:28 +0800 Subject: [PATCH 12/21] update templatingTask test --- packages/cli/src/commands/create-plugin/createPlugin.ts | 5 +---- packages/cli/src/lib/tasks.test.ts | 5 +++++ packages/cli/templates/default-plugin/package.json.hbs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 9b97746c0e..00539a4b33 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -228,10 +228,7 @@ export default async (cmd: Command) => { ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` : `plugin-${answers.id}`; const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; - const npmRegistry = - cmd.npmRegistry && cmd.scope - ? `"${scopeName}:registry": "${cmd.npmRegistry}"` - : ''; + 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'); diff --git a/packages/cli/src/lib/tasks.test.ts b/packages/cli/src/lib/tasks.test.ts index 871d5a4d37..2a2f111d4b 100644 --- a/packages/cli/src/lib/tasks.test.ts +++ b/packages/cli/src/lib/tasks.test.ts @@ -37,6 +37,11 @@ describe('templatingTask', () => { try { await templatingTask(tmplDir, destDir, { version: '0.0.0', + backstageVersiona: '0.0.0', + scopeName: '@backstage', + name: '@backstage/plugin-test', + privatePackage: true, + npmRegistry: 'https://registry.npmjs.org/', }); await expect( diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 5e59b6fa0d..043e54b914 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -7,7 +7,7 @@ {{#if privatePackage}} "private": {{privatePackage}}, {{/if}} "publishConfig": { -{{#if npmRegistry}} {{{npmRegistry}}}, +{{#if npmRegistry}} "{{scopeName}}:registry": "{{npmRegistry}}", {{/if}} "access": "public", "main": "dist/index.esm.js", From 820304af3cd4fc74e3963159d070cc573725a109 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Wed, 16 Sep 2020 15:01:12 +0800 Subject: [PATCH 13/21] revert templatingTask test --- packages/cli/src/lib/tasks.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/cli/src/lib/tasks.test.ts b/packages/cli/src/lib/tasks.test.ts index 2a2f111d4b..871d5a4d37 100644 --- a/packages/cli/src/lib/tasks.test.ts +++ b/packages/cli/src/lib/tasks.test.ts @@ -37,11 +37,6 @@ describe('templatingTask', () => { try { await templatingTask(tmplDir, destDir, { version: '0.0.0', - backstageVersiona: '0.0.0', - scopeName: '@backstage', - name: '@backstage/plugin-test', - privatePackage: true, - npmRegistry: 'https://registry.npmjs.org/', }); await expect( From d0104e2052da08111c8fc386693c5714d675287e Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Wed, 16 Sep 2020 15:19:20 +0800 Subject: [PATCH 14/21] revert templatingTask --- packages/cli/src/lib/tasks.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 75794a1ba6..0753301b78 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -91,6 +91,15 @@ export async function templatingTask( ); }); }); + } else { + await Task.forItem('copying', basename(file), async () => { + await fs.copyFile(file, destinationFile).catch(error => { + const destination = destinationFile; + throw new Error( + `Failed to copy file to ${destination} : ${error.message}`, + ); + }); + }); } } } From 5603f1430dcc8254f05715116fe0a7d14075d7c9 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Thu, 17 Sep 2020 16:52:34 +0800 Subject: [PATCH 15/21] remove scope from registry --- packages/cli/src/commands/create-plugin/createPlugin.ts | 2 -- packages/cli/templates/default-plugin/package.json.hbs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 00539a4b33..9b96dbc79d 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -227,7 +227,6 @@ export default async (cmd: Command) => { const name = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}/plugin-${answers.id}` : `plugin-${answers.id}`; - const scopeName = cmd.scope ? `@${cmd.scope.replace(/^@/, '')}` : ''; const npmRegistry = cmd.npmRegistry && cmd.scope ? cmd.npmRegistry : ''; const privatePackage = cmd.private === false ? false : true; const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json')); @@ -255,7 +254,6 @@ export default async (cmd: Command) => { ...answers, version, backstageVersion, - scopeName, name, privatePackage, npmRegistry, diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 043e54b914..52cffeb621 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -7,7 +7,7 @@ {{#if privatePackage}} "private": {{privatePackage}}, {{/if}} "publishConfig": { -{{#if npmRegistry}} "{{scopeName}}:registry": "{{npmRegistry}}", +{{#if npmRegistry}} "registry": "{{npmRegistry}}", {{/if}} "access": "public", "main": "dist/index.esm.js", From 885e9a63f6e7603d5fb9ca6f818c9aa5b3e31e1d Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Fri, 18 Sep 2020 13:10:50 +0800 Subject: [PATCH 16/21] use version from lerna.json or fallback to 0.1.0 --- packages/cli/src/commands/create-plugin/createPlugin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 9b96dbc79d..247812b6fd 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -237,7 +237,9 @@ export default async (cmd: Command) => { ? paths.resolveTargetRoot('plugins', answers.id) : paths.resolveTargetRoot(answers.id); const ownerIds = parseOwnerIds(answers.owner); - const version = backstageVersion; + const { version } = isMonoRepo + ? await fs.readJson(paths.resolveTargetRoot('lerna.json')) + : { version: '0.1.0' }; Task.log(); Task.log('Creating the plugin...'); From 6a60d609e8f4f05e4f56d5be788c078f2ba3bba6 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 17 Sep 2020 15:31:05 +0200 Subject: [PATCH 17/21] feat: move add example components to a more visible space and change behavior Previously the button was displayed every time the table was empty. This event happend during filtering, causing the layout to switch around. Now we check if the catalog is completly empty instead. In addition the button is now at the top of the page and not part of the table. If no example locations are configured, it is completly hidden. --- .../src/components/CatalogPage/CatalogPage.tsx | 18 +++++++++++++++++- .../CatalogTable/CatalogTable.test.tsx | 2 -- .../components/CatalogTable/CatalogTable.tsx | 12 +----------- .../src/filter/EntityFilterGroupsProvider.tsx | 3 +++ plugins/catalog/src/filter/context.ts | 1 + .../catalog/src/filter/useFilteredEntities.ts | 1 + 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index db490cf542..ea99753c14 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -46,6 +46,9 @@ const useStyles = makeStyles(theme => ({ gridTemplateColumns: '250px 1fr', gridColumnGap: theme.spacing(2), }, + buttonSpacing: { + marginLeft: theme.spacing(2), + }, })); const CatalogPageContents = () => { @@ -56,6 +59,7 @@ const CatalogPageContents = () => { reload, matchingEntities, availableTags, + isCatalogEmpty, } = useFilteredEntities(); const configApi = useApi(configApiRef); const catalogApi = useApi(catalogApiRef); @@ -141,6 +145,9 @@ const CatalogPageContents = () => { [isStarredEntity, userId, orgName], ); + const showAddExampleEntities = + configApi.has('catalog.exampleEntityLocations') && isCatalogEmpty; + return ( { > Create Component + {showAddExampleEntities && ( + + )} All your software catalog entities
@@ -174,7 +191,6 @@ const CatalogPageContents = () => { entities={matchingEntities} loading={loading} error={error} - onAddMockData={addMockData} />
diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index d1eef2a005..46d6010f2f 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -46,7 +46,6 @@ describe('CatalogTable component', () => { entities={[]} loading={false} error={{ code: 'error' }} - onAddMockData={() => {}} />, ), ); @@ -63,7 +62,6 @@ describe('CatalogTable component', () => { titlePreamble="Owned" entities={entities} loading={false} - onAddMockData={() => {}} />, ), ); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index cd4df13775..f5885145b3 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -16,11 +16,10 @@ import { Entity, LocationSpec } from '@backstage/catalog-model'; import { Table, TableColumn, TableProps } from '@backstage/core'; import { Chip, Link } from '@material-ui/core'; -import Add from '@material-ui/icons/Add'; import Edit from '@material-ui/icons/Edit'; import GitHub from '@material-ui/icons/GitHub'; import { Alert } from '@material-ui/lab'; -import React, { Dispatch } from 'react'; +import React from 'react'; import { generatePath, Link as RouterLink } from 'react-router-dom'; import { findLocationForEntityMeta } from '../../data/utils'; import { useStarredEntities } from '../../hooks/useStarredEntites'; @@ -87,7 +86,6 @@ type CatalogTableProps = { titlePreamble: string; loading: boolean; error?: any; - onAddMockData: Dispatch; }; export const CatalogTable = ({ @@ -95,7 +93,6 @@ export const CatalogTable = ({ loading, error, titlePreamble, - onAddMockData, }: CatalogTableProps) => { const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); @@ -151,13 +148,6 @@ export const CatalogTable = ({ onClick: () => toggleStarredEntity(rowData), }; }, - { - icon: () => , - tooltip: 'Add example components', - isFreeAction: true, - onClick: onAddMockData, - hidden: !(entities && entities.length === 0), - }, ]; return ( diff --git a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx index 94bc428a45..5c3a67d9d0 100644 --- a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx @@ -62,6 +62,7 @@ function useProvideEntityFilters(): FilterGroupsContext { }>({}); const [matchingEntities, setMatchingEntities] = useState([]); const [availableTags, setAvailableTags] = useState([]); + const [isCatalogEmpty, setCatalogEmpty] = useState(false); useEffect(() => { doReload(); @@ -86,6 +87,7 @@ function useProvideEntityFilters(): FilterGroupsContext { ), ); setAvailableTags(collectTags(entities)); + setCatalogEmpty(entities !== undefined && entities.length === 0); }, [entities, error]); const register = useCallback( @@ -143,6 +145,7 @@ function useProvideEntityFilters(): FilterGroupsContext { filterGroupStates, matchingEntities, availableTags, + isCatalogEmpty, }; } diff --git a/plugins/catalog/src/filter/context.ts b/plugins/catalog/src/filter/context.ts index 838ec8d532..c025480fa6 100644 --- a/plugins/catalog/src/filter/context.ts +++ b/plugins/catalog/src/filter/context.ts @@ -33,6 +33,7 @@ export type FilterGroupsContext = { filterGroupStates: { [filterGroupId: string]: FilterGroupStates }; matchingEntities: Entity[]; availableTags: string[]; + isCatalogEmpty: boolean; }; /** diff --git a/plugins/catalog/src/filter/useFilteredEntities.ts b/plugins/catalog/src/filter/useFilteredEntities.ts index 138a7547c0..2d7dcfd89d 100644 --- a/plugins/catalog/src/filter/useFilteredEntities.ts +++ b/plugins/catalog/src/filter/useFilteredEntities.ts @@ -31,6 +31,7 @@ export function useFilteredEntities() { error: context.error, matchingEntities: context.matchingEntities, availableTags: context.availableTags, + isCatalogEmpty: context.isCatalogEmpty, reload: context.reload, }; } From c742a38ddbf66d84f575f9175fa4f018d5230b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 21 Sep 2020 08:43:51 +0200 Subject: [PATCH 18/21] fix(gcp-projects): some postponed cleanup of the plugin code (#2523) --- plugins/gcp-projects/src/api/GCPClient.ts | 124 ------------------ .../src/api/{GCPApi.ts => GcpApi.ts} | 18 ++- plugins/gcp-projects/src/api/GcpClient.ts | 98 ++++++++++++++ plugins/gcp-projects/src/api/index.ts | 4 +- .../NewProjectPage/NewProjectPage.tsx | 17 ++- .../ProjectDetailsPage/ProjectDetailsPage.tsx | 49 +++---- .../ProjectListPage/ProjectListPage.tsx | 42 +++--- plugins/gcp-projects/src/plugin.ts | 27 ++-- 8 files changed, 171 insertions(+), 208 deletions(-) delete mode 100644 plugins/gcp-projects/src/api/GCPClient.ts rename plugins/gcp-projects/src/api/{GCPApi.ts => GcpApi.ts} (69%) create mode 100644 plugins/gcp-projects/src/api/GcpClient.ts diff --git a/plugins/gcp-projects/src/api/GCPClient.ts b/plugins/gcp-projects/src/api/GCPClient.ts deleted file mode 100644 index dc4fe21132..0000000000 --- a/plugins/gcp-projects/src/api/GCPClient.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { GCPApi } from './GCPApi'; -import { Project, Operation, Status } from './types'; - -const BaseURL = - 'https://content-cloudresourcemanager.googleapis.com/v1/projects'; - -export class GCPClient implements GCPApi { - async listProjects({ token }: { token: string }): Promise { - const response = await fetch(BaseURL, { - headers: new Headers({ - Accept: '*/*', - Authorization: `Bearer ${token}`, - }), - }); - - if (!response.ok) { - return [ - { - name: 'Error', - projectNumber: 'Response status is not OK', - projectId: 'Error', - lifecycleState: 'error', - createTime: 'Error', - }, - ]; - } - - const data = await response.json(); - - return data.projects; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getProject( - projectId: string, - token: Promise, - ): Promise { - const url = `${BaseURL}/${projectId}`; - const response = await fetch(url, { - headers: new Headers({ - Authorization: `Bearer ${await token}`, - }), - }); - - const dataBlank: Project = { - name: 'Error', - projectNumber: `Response status is ${response.status}`, - projectId: 'Error', - lifecycleState: 'error', - createTime: 'Error', - }; - - if (!response.ok) { - return dataBlank; - } - - const data = await response.json(); - - const newData: Project = data; - - return newData; - } - - async createProject( - projectName: string, - projectId: string, - token: string, - ): Promise { - const status: Status = { - code: 0, - message: '', - details: [], - }; - - const op: Operation = { - name: '', - metadata: '', - done: true, - error: status, - response: '', - }; - - const newProject: Project = { - name: projectName, - projectId: projectId, - }; - - const body = JSON.stringify(newProject); - - const response = await fetch(BaseURL, { - headers: new Headers({ - Accept: '*/*', - Authorization: `Bearer ${token}`, - }), - body: body, - method: 'POST', - }); - - if (!response.ok) { - status.code = response.status; - return op; - } - - const data = await response.json(); - - return data; - } -} diff --git a/plugins/gcp-projects/src/api/GCPApi.ts b/plugins/gcp-projects/src/api/GcpApi.ts similarity index 69% rename from plugins/gcp-projects/src/api/GCPApi.ts rename to plugins/gcp-projects/src/api/GcpApi.ts index 95b686815e..24ad1a93cc 100644 --- a/plugins/gcp-projects/src/api/GCPApi.ts +++ b/plugins/gcp-projects/src/api/GcpApi.ts @@ -17,18 +17,16 @@ import { createApiRef } from '@backstage/core'; import { Project, Operation } from './types'; -export const GCPApiRef = createApiRef({ +export const gcpApiRef = createApiRef({ id: 'plugin.gcpprojects.service', description: 'Used by the GCP Projects plugin to make requests', }); -export type GCPApi = { - listProjects: ({ token }: { token: string }) => Promise; - getProject: (projectId: string, token: Promise) => Promise; - createProject: ( - projectName: string, - projectId: string, - owner: string, - token: string, - ) => Promise; +export type GcpApi = { + listProjects(): Promise; + getProject(projectId: string): Promise; + createProject(options: { + projectId: string; + projectName: string; + }): Promise; }; diff --git a/plugins/gcp-projects/src/api/GcpClient.ts b/plugins/gcp-projects/src/api/GcpClient.ts new file mode 100644 index 0000000000..00bb859219 --- /dev/null +++ b/plugins/gcp-projects/src/api/GcpClient.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { OAuthApi } from '@backstage/core'; +import { GcpApi } from './GcpApi'; +import { Operation, Project } from './types'; + +const BASE_URL = + 'https://content-cloudresourcemanager.googleapis.com/v1/projects'; + +export class GcpClient implements GcpApi { + constructor(private readonly googleAuthApi: OAuthApi) {} + + async listProjects(): Promise { + const response = await fetch(BASE_URL, { + headers: { + Accept: '*/*', + Authorization: `Bearer ${await this.getToken()}`, + }, + }); + + if (!response.ok) { + throw new Error( + `List request failed to ${BASE_URL} with ${response.status} ${response.statusText}`, + ); + } + + const { projects } = await response.json(); + return projects; + } + + async getProject(projectId: string): Promise { + const url = `${BASE_URL}/${projectId}`; + const response = await fetch(url, { + headers: { + Authorization: `Bearer ${await this.getToken()}`, + }, + }); + + if (!response.ok) { + throw new Error( + `Get request failed to ${url} with ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } + + async createProject(options: { + projectId: string; + projectName: string; + }): Promise { + const newProject: Project = { + name: options.projectName, + projectId: options.projectId, + }; + + const response = await fetch(BASE_URL, { + method: 'POST', + headers: { + Accept: '*/*', + Authorization: `Bearer ${await this.getToken()}`, + }, + body: JSON.stringify(newProject), + }); + + if (!response.ok) { + throw new Error( + `Create request failed to ${BASE_URL} with ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } + + async getToken(): Promise { + // NOTE(freben): There's a .read-only variant of this scope that we could + // use for readonly operations, but that means we would ask the user for a + // second auth during creation and I decided to keep the wider scope for + // all ops for now + return this.googleAuthApi.getAccessToken( + 'https://www.googleapis.com/auth/cloud-platform', + ); + } +} diff --git a/plugins/gcp-projects/src/api/index.ts b/plugins/gcp-projects/src/api/index.ts index 51f617f19c..8e842c8e55 100644 --- a/plugins/gcp-projects/src/api/index.ts +++ b/plugins/gcp-projects/src/api/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export * from './GCPApi'; -export * from './GCPClient'; +export * from './GcpApi'; +export * from './GcpClient'; export * from './types'; diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index 0149f8ac92..f03149bc1a 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -14,24 +14,23 @@ * limitations under the License. */ -import React, { FC, useState } from 'react'; -import { Grid, Button, TextField } from '@material-ui/core'; - import { - InfoCard, Content, ContentHeader, + Header, + HeaderLabel, + InfoCard, + Page, + pageTheme, SimpleStepper, SimpleStepperStep, StructuredMetadataTable, - HeaderLabel, - Page, - Header, - pageTheme, SupportButton, } from '@backstage/core'; +import { Button, Grid, TextField } from '@material-ui/core'; +import React, { useState } from 'react'; -export const Project: FC<{}> = () => { +export const Project = () => { const [projectName, setProjectName] = useState(''); const [projectId, setProjectId] = useState(''); const [disabled, setDisabled] = useState(true); diff --git a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx index 41d0b11019..3dfb4e886b 100644 --- a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx @@ -14,6 +14,17 @@ * limitations under the License. */ +import { + Content, + ContentHeader, + Header, + HeaderLabel, + Page, + pageTheme, + SupportButton, + useApi, + WarningPanel, +} from '@backstage/core'; import { Button, ButtonGroup, @@ -27,20 +38,9 @@ import { Theme, Typography, } from '@material-ui/core'; -import { - useApi, - googleAuthApiRef, - HeaderLabel, - Page, - Header, - pageTheme, - SupportButton, - Content, - ContentHeader, -} from '@backstage/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { GCPApiRef } from '../../api'; +import { gcpApiRef } from '../../api'; const useStyles = makeStyles(theme => ({ root: { @@ -56,34 +56,27 @@ const useStyles = makeStyles(theme => ({ })); const DetailsPage = () => { - const api = useApi(GCPApiRef); - const googleApi = useApi(googleAuthApiRef); - const token = googleApi.getAccessToken( - 'https://www.googleapis.com/auth/cloud-platform.read-only', - ); - + const api = useApi(gcpApiRef); const classes = useStyles(); - const status = useAsync( - () => + + const { loading, error, value: details } = useAsync( + async () => api.getProject( decodeURIComponent(location.search.split('projectId=')[1]), - token, ), [location.search], ); - if (status.loading) { + if (loading) { return ; - } else if (status.error) { + } else if (error) { return ( - - Failed to load build, {status.error.message} - + + {error.toString()} + ); } - const details = status.value; - return (
diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 7e09a307c9..542d03d5fd 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -17,18 +17,19 @@ // NEEDS WORK import { - Link, - useApi, - googleAuthApiRef, - HeaderLabel, - Page, - Header, - pageTheme, - SupportButton, Content, ContentHeader, + Header, + HeaderLabel, + Link, + Page, + pageTheme, + SupportButton, + useApi, + WarningPanel, } from '@backstage/core'; import { + Button, LinearProgress, Paper, Table, @@ -38,11 +39,10 @@ import { TableRow, Tooltip, Typography, - Button, } from '@material-ui/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { GCPApiRef, Project } from '../../api'; +import { gcpApiRef, Project } from '../../api'; const LongText = ({ text, max }: { text: string; max: number }) => { if (text.length < max) { @@ -63,27 +63,17 @@ const labels = ( ); const PageContents = () => { - const api = useApi(GCPApiRef); - const googleApi = useApi(googleAuthApiRef); + const api = useApi(gcpApiRef); - const { loading, error, value } = useAsync(async () => { - const token = await googleApi.getAccessToken( - 'https://www.googleapis.com/auth/cloud-platform.read-only', - ); - - const projects = api.listProjects({ token }); - return projects; - }); + const { loading, error, value } = useAsync(() => api.listProjects()); if (loading) { return ; - } - - if (error) { + } else if (error) { return ( - - {error.message}{' '} - + + {error.toString()} + ); } diff --git a/plugins/gcp-projects/src/plugin.ts b/plugins/gcp-projects/src/plugin.ts index 41aacde597..2baf3bce7d 100644 --- a/plugins/gcp-projects/src/plugin.ts +++ b/plugins/gcp-projects/src/plugin.ts @@ -15,34 +15,43 @@ */ import { + createApiFactory, createPlugin, createRouteRef, - createApiFactory, + googleAuthApiRef, } from '@backstage/core'; -import { ProjectListPage } from './components/ProjectListPage'; -import { ProjectDetailsPage } from './components/ProjectDetailsPage'; +import { gcpApiRef, GcpClient } from './api'; import { NewProjectPage } from './components/NewProjectPage'; -import { GCPApiRef, GCPClient } from './api'; +import { ProjectDetailsPage } from './components/ProjectDetailsPage'; +import { ProjectListPage } from './components/ProjectListPage'; export const rootRouteRef = createRouteRef({ path: '/gcp-projects', title: 'GCP Projects', }); -export const ProjectRouteRef = createRouteRef({ +export const projectRouteRef = createRouteRef({ path: '/gcp-projects/project', title: 'GCP Project Page', }); -export const NewProjectRouteRef = createRouteRef({ +export const newProjectRouteRef = createRouteRef({ path: '/gcp-projects/new', title: 'GCP Project Page', }); export const plugin = createPlugin({ id: 'gcp-projects', - apis: [createApiFactory(GCPApiRef, new GCPClient())], + apis: [ + createApiFactory({ + api: gcpApiRef, + deps: { googleAuthApi: googleAuthApiRef }, + factory({ googleAuthApi }) { + return new GcpClient(googleAuthApi); + }, + }), + ], register({ router }) { router.addRoute(rootRouteRef, ProjectListPage); - router.addRoute(ProjectRouteRef, ProjectDetailsPage); - router.addRoute(NewProjectRouteRef, NewProjectPage); + router.addRoute(projectRouteRef, ProjectDetailsPage); + router.addRoute(newProjectRouteRef, NewProjectPage); }, }); From 4d791599c3abc6bf4099704c6b7d6a0b2aaefe3b Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 21 Sep 2020 09:13:16 +0200 Subject: [PATCH 19/21] feat(catalog): show only components --- plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx index 94bc428a45..94f1fe3b70 100644 --- a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx @@ -47,7 +47,7 @@ export const EntityFilterGroupsProvider = ({ function useProvideEntityFilters(): FilterGroupsContext { const catalogApi = useApi(catalogApiRef); const [{ value: entities, error }, doReload] = useAsyncFn(() => - catalogApi.getEntities(), + catalogApi.getEntities({ kind: 'Component' }), ); const filterGroups = useRef<{ From 60e9799075deb7fc6b8f5ea604211c8e70b5d483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 21 Sep 2020 10:41:27 +0200 Subject: [PATCH 20/21] [microsite] tweaking CNCF logo styling (#2529) --- microsite/pages/en/index.js | 2 +- microsite/static/css/custom.css | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index 37298ae8e2..6e649aa574 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -471,8 +471,8 @@ class Index extends React.Component { Cloud Native Computing Foundation {' '} sandbox project +
-
diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index 00a102ba00..79b8fd9de4 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -1072,6 +1072,7 @@ code { } .cncf-block { + padding-top: 40px; text-align: center; } @@ -1080,4 +1081,5 @@ code { width: 100%; height: 100px; margin-bottom: 40px; + margin-top: 20px; } From 2af1b2b5a7f1d8dfc2fab6fdfb65c97a4ae992ac Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 21 Sep 2020 12:45:26 +0200 Subject: [PATCH 21/21] core/package.json: pin core-api to same version as core `@backstage/core-api` is an internal package that should never be depended on directly. The version of `@backstage/core` should be what determines the version of `@backstage/core`, not the lockfile. --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 3ed730e0f3..440f9f05b0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.1-alpha.22", - "@backstage/core-api": "^0.1.1-alpha.22", + "@backstage/core-api": "0.1.1-alpha.22", "@backstage/theme": "^0.1.1-alpha.22", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1",