cli: remove deprecated commands
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the following deprecated commands:
|
||||
|
||||
- `create`: Use `backstage-cli new` instead
|
||||
- `create-plugin`: Use `backstage-cli new` instead
|
||||
- `plugin:diff`: Use `backstage-cli fix` instead
|
||||
- `test`: Use `backstage-cli repo test` or `backstage-cli package test` instead
|
||||
- `versions:check`: Use `yarn dedupe` or `yarn-deduplicate` instead
|
||||
- `clean`: Use `backstage-cli package clean` instead
|
||||
@@ -13,7 +13,6 @@ Options:
|
||||
|
||||
Commands:
|
||||
new [options]
|
||||
test
|
||||
config:docs [options]
|
||||
config:print [options]
|
||||
config:check [options]
|
||||
@@ -23,7 +22,6 @@ Commands:
|
||||
migrate [command]
|
||||
versions:bump [options]
|
||||
versions:migrate [options]
|
||||
clean
|
||||
build-workspace [options] <workspace-dir> [packages...]
|
||||
create-github-app <github-org>
|
||||
info
|
||||
@@ -40,15 +38,6 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli clean`
|
||||
|
||||
```
|
||||
Usage: backstage-cli clean [options]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli config:check`
|
||||
|
||||
```
|
||||
@@ -481,120 +470,6 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli test`
|
||||
|
||||
```
|
||||
Usage: backstage-cli [--config=<pathToConfigFile>] [TestPathPattern]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
--version
|
||||
--all
|
||||
--automock
|
||||
-b, --bail
|
||||
--cache
|
||||
--cacheDirectory
|
||||
--changedFilesWithAncestor
|
||||
--changedSince
|
||||
--ci
|
||||
--clearCache
|
||||
--clearMocks
|
||||
--collectCoverage
|
||||
--collectCoverageFrom
|
||||
--color
|
||||
--colors
|
||||
-c, --config
|
||||
--coverage
|
||||
--coverageDirectory
|
||||
--coveragePathIgnorePatterns
|
||||
--coverageProvider
|
||||
--coverageReporters
|
||||
--coverageThreshold
|
||||
--debug
|
||||
--detectLeaks
|
||||
--detectOpenHandles
|
||||
--env
|
||||
--errorOnDeprecated
|
||||
-e, --expand
|
||||
--filter
|
||||
--findRelatedTests
|
||||
--forceExit
|
||||
--globalSetup
|
||||
--globalTeardown
|
||||
--globals
|
||||
--haste
|
||||
--ignoreProjects
|
||||
--init
|
||||
--injectGlobals
|
||||
--json
|
||||
--lastCommit
|
||||
--listTests
|
||||
--logHeapUsage
|
||||
--maxConcurrency
|
||||
-w, --maxWorkers
|
||||
--moduleDirectories
|
||||
--moduleFileExtensions
|
||||
--moduleNameMapper
|
||||
--modulePathIgnorePatterns
|
||||
--modulePaths
|
||||
--noStackTrace
|
||||
--notify
|
||||
--notifyMode
|
||||
-o, --onlyChanged
|
||||
-f, --onlyFailures
|
||||
--openHandlesTimeout
|
||||
--outputFile
|
||||
--passWithNoTests
|
||||
--preset
|
||||
--prettierPath
|
||||
--projects
|
||||
--randomize
|
||||
--reporters
|
||||
--resetMocks
|
||||
--resetModules
|
||||
--resolver
|
||||
--restoreMocks
|
||||
--rootDir
|
||||
--roots
|
||||
-i, --runInBand
|
||||
--runTestsByPath
|
||||
--runner
|
||||
--seed
|
||||
--selectProjects
|
||||
--setupFiles
|
||||
--setupFilesAfterEnv
|
||||
--shard
|
||||
--showConfig
|
||||
--showSeed
|
||||
--silent
|
||||
--skipFilter
|
||||
--snapshotSerializers
|
||||
--testEnvironment
|
||||
--testEnvironmentOptions
|
||||
--testFailureExitCode
|
||||
--testLocationInResults
|
||||
--testMatch
|
||||
-t, --testNamePattern
|
||||
--testPathIgnorePatterns
|
||||
--testPathPattern
|
||||
--testRegex
|
||||
--testResultsProcessor
|
||||
--testRunner
|
||||
--testSequencer
|
||||
--testTimeout
|
||||
--transform
|
||||
--transformIgnorePatterns
|
||||
--unmockedModulePathPatterns
|
||||
-u, --updateSnapshot
|
||||
--useStderr
|
||||
--verbose
|
||||
--watch
|
||||
--watchAll
|
||||
--watchPathIgnorePatterns
|
||||
--watchman
|
||||
--workerThreads
|
||||
```
|
||||
|
||||
### `backstage-cli versions:bump`
|
||||
|
||||
```
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { movePlugin } from './createPlugin';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const id = 'testPluginMock';
|
||||
|
||||
describe('createPlugin', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
describe('movePlugin', () => {
|
||||
it('should move the temporary plugin directory to its final place', async () => {
|
||||
mockDir.setContent({
|
||||
[id]: {},
|
||||
});
|
||||
const tempDir = mockDir.resolve(id);
|
||||
const pluginDir = mockDir.resolve('test-temp/plugins', id);
|
||||
|
||||
await movePlugin(tempDir, pluginDir, id);
|
||||
await expect(fs.pathExists(pluginDir)).resolves.toBe(true);
|
||||
expect(pluginDir).toMatch(path.join('', 'plugins', id));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { promisify } from 'util';
|
||||
import chalk from 'chalk';
|
||||
import inquirer, { Answers, Question } from 'inquirer';
|
||||
import { exec as execCb } from 'child_process';
|
||||
import { join as joinPath, resolve as resolvePath } from 'path';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import os from 'os';
|
||||
import { OptionValues } from 'commander';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import {
|
||||
addCodeownersEntry,
|
||||
getCodeownersFilePath,
|
||||
parseOwnerIds,
|
||||
} from '../../lib/codeowners';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { Task, templatingTask } from '../../lib/tasks';
|
||||
import { Lockfile } from '../../lib/versioning';
|
||||
import { createPackageVersionProvider } from '../../lib/version';
|
||||
|
||||
const exec = promisify(execCb);
|
||||
|
||||
async function checkExists(destination: string) {
|
||||
await Task.forItem('checking', destination, async () => {
|
||||
if (await fs.pathExists(destination)) {
|
||||
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`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const sortObjectByKeys = (obj: { [name in string]: string }) => {
|
||||
return Object.keys(obj)
|
||||
.sort()
|
||||
.reduce((result, key: string) => {
|
||||
result[key] = obj[key];
|
||||
return result;
|
||||
}, {} as { [name in string]: string });
|
||||
};
|
||||
|
||||
export const capitalize = (str: string): string =>
|
||||
str.charAt(0).toUpperCase() + str.slice(1);
|
||||
|
||||
export const addExportStatement = async (
|
||||
file: string,
|
||||
exportStatement: string,
|
||||
) => {
|
||||
const newContents = fs
|
||||
.readFileSync(file, 'utf8')
|
||||
.split('\n')
|
||||
.filter(Boolean) // get rid of empty lines
|
||||
.concat([exportStatement])
|
||||
.concat(['']) // newline at end of file
|
||||
.join('\n');
|
||||
|
||||
await fs.writeFile(file, newContents, 'utf8');
|
||||
};
|
||||
|
||||
export async function addPluginDependencyToApp(
|
||||
rootDir: string,
|
||||
pluginPackage: string,
|
||||
versionStr: string,
|
||||
) {
|
||||
const packageFilePath = 'packages/app/package.json';
|
||||
const packageFile = resolvePath(rootDir, packageFilePath);
|
||||
|
||||
await Task.forItem('processing', packageFilePath, async () => {
|
||||
const packageFileContent = await fs.readFile(packageFile, 'utf-8');
|
||||
const packageFileJson = JSON.parse(packageFileContent);
|
||||
const dependencies = packageFileJson.dependencies;
|
||||
|
||||
if (dependencies[pluginPackage]) {
|
||||
throw new Error(
|
||||
`Plugin ${pluginPackage} already exists in ${packageFile}`,
|
||||
);
|
||||
}
|
||||
|
||||
dependencies[pluginPackage] = `^${versionStr}`;
|
||||
packageFileJson.dependencies = sortObjectByKeys(dependencies);
|
||||
const newContents = `${JSON.stringify(packageFileJson, null, 2)}\n`;
|
||||
|
||||
await fs.writeFile(packageFile, newContents, 'utf-8').catch(error => {
|
||||
throw new Error(
|
||||
`Failed to add plugin as dependency to app: ${packageFile}: ${error.message}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function addPluginExtensionToApp(
|
||||
pluginId: string,
|
||||
extensionName: string,
|
||||
pluginPackage: string,
|
||||
) {
|
||||
const pluginsFilePath = paths.resolveTargetRoot('packages/app/src/App.tsx');
|
||||
if (!(await fs.pathExists(pluginsFilePath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.forItem('processing', pluginsFilePath, async () => {
|
||||
const content = await fs.readFile(pluginsFilePath, 'utf8');
|
||||
const revLines = content.split('\n').reverse();
|
||||
|
||||
const lastImportIndex = revLines.findIndex(line =>
|
||||
line.match(/ from ("|').*("|')/),
|
||||
);
|
||||
const lastRouteIndex = revLines.findIndex(line =>
|
||||
line.match(/<\/FlatRoutes/),
|
||||
);
|
||||
|
||||
if (lastImportIndex !== -1 && lastRouteIndex !== -1) {
|
||||
revLines.splice(
|
||||
lastImportIndex,
|
||||
0,
|
||||
`import { ${extensionName} } from '${pluginPackage}';`,
|
||||
);
|
||||
const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? [];
|
||||
revLines.splice(
|
||||
lastRouteIndex + 1,
|
||||
0,
|
||||
`${indentation}<Route path="/${pluginId}" element={<${extensionName} />}/>`,
|
||||
);
|
||||
|
||||
const newContent = revLines.reverse().join('\n');
|
||||
await fs.writeFile(pluginsFilePath, newContent, 'utf8');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function cleanUp(tempDir: string) {
|
||||
await Task.forItem('remove', 'temporary directory', async () => {
|
||||
await fs.remove(tempDir);
|
||||
});
|
||||
}
|
||||
|
||||
async function buildPlugin(pluginFolder: string) {
|
||||
const commands = [
|
||||
'yarn install',
|
||||
'yarn lint --fix',
|
||||
'yarn tsc',
|
||||
'yarn build',
|
||||
];
|
||||
for (const command of commands) {
|
||||
try {
|
||||
await Task.forItem('executing', command, async () => {
|
||||
process.chdir(pluginFolder);
|
||||
await exec(command);
|
||||
}).catch(error => {
|
||||
process.stdout.write(error.stderr);
|
||||
process.stdout.write(error.stdout);
|
||||
throw new Error(
|
||||
`Warning: Could not execute command ${chalk.cyan(command)}`,
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
Task.error(error.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function movePlugin(
|
||||
tempDir: string,
|
||||
destination: string,
|
||||
id: string,
|
||||
) {
|
||||
await Task.forItem('moving', id, async () => {
|
||||
await fs.move(tempDir, destination).catch(error => {
|
||||
throw new Error(
|
||||
`Failed to move plugin from ${tempDir} to ${destination}: ${error.message}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default async (opts: OptionValues) => {
|
||||
const codeownersPath = await getCodeownersFilePath(paths.targetRoot);
|
||||
|
||||
const questions: Question[] = [
|
||||
{
|
||||
type: 'input',
|
||||
name: 'id',
|
||||
message: chalk.blue('Enter an ID for the plugin [required]'),
|
||||
validate: (value: any) => {
|
||||
if (!value) {
|
||||
return chalk.red('Please enter an ID for the plugin');
|
||||
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
|
||||
return chalk.red(
|
||||
'Plugin IDs must be lowercase and contain only letters, digits, and dashes.',
|
||||
);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (codeownersPath) {
|
||||
questions.push({
|
||||
type: 'input',
|
||||
name: 'owner',
|
||||
message: chalk.blue(
|
||||
'Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional]',
|
||||
),
|
||||
validate: (value: any) => {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ownerIds = parseOwnerIds(value);
|
||||
if (!ownerIds) {
|
||||
return chalk.red(
|
||||
'The owner must be a space separated list of team names (e.g. @org/team-name), usernames (e.g. @username), or the email addresses of users (e.g. user@example.com).',
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const answers: Answers = await inquirer.prompt(questions);
|
||||
const pluginId =
|
||||
opts.backend && !answers.id.endsWith('-backend')
|
||||
? `${answers.id}-backend`
|
||||
: answers.id;
|
||||
|
||||
const name = opts.scope
|
||||
? `@${opts.scope.replace(/^@/, '')}/plugin-${pluginId}`
|
||||
: `plugin-${pluginId}`;
|
||||
const pluginVar = `${camelCase(answers.id)}Plugin`;
|
||||
const extensionName = `${upperFirst(camelCase(answers.id))}Page`;
|
||||
const npmRegistry = opts.npmRegistry && opts.scope ? opts.npmRegistry : '';
|
||||
const privatePackage = opts.private === false ? false : true;
|
||||
const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json'));
|
||||
const appPackage = paths.resolveTargetRoot('packages/app');
|
||||
const templateDir = paths.resolveOwn(
|
||||
opts.backend
|
||||
? 'templates/default-backend-plugin'
|
||||
: 'templates/default-plugin',
|
||||
);
|
||||
const pluginDir = isMonoRepo
|
||||
? paths.resolveTargetRoot('plugins', pluginId)
|
||||
: paths.resolveTargetRoot(pluginId);
|
||||
const { version: pluginVersion } = isMonoRepo
|
||||
? await fs.readJson(paths.resolveTargetRoot('lerna.json'))
|
||||
: { version: '0.1.0' };
|
||||
const license = opts.license ?? 'Apache-2.0';
|
||||
|
||||
let lockfile: Lockfile | undefined;
|
||||
try {
|
||||
lockfile = await Lockfile.load(paths.resolveTargetRoot('yarn.lock'));
|
||||
} catch (error) {
|
||||
console.warn(`No yarn.lock available, ${error}`);
|
||||
}
|
||||
|
||||
Task.log();
|
||||
Task.log('Creating the plugin...');
|
||||
|
||||
Task.section('Checking if the plugin ID is available');
|
||||
await checkExists(pluginDir);
|
||||
|
||||
Task.section('Creating a temporary plugin directory');
|
||||
const tempDir = await fs.mkdtemp(
|
||||
joinPath(os.tmpdir(), `backstage-plugin-${pluginId}`),
|
||||
);
|
||||
|
||||
try {
|
||||
Task.section('Preparing files');
|
||||
|
||||
await templatingTask(
|
||||
templateDir,
|
||||
tempDir,
|
||||
{
|
||||
...answers,
|
||||
pluginVar,
|
||||
pluginVersion,
|
||||
extensionName,
|
||||
name,
|
||||
privatePackage,
|
||||
npmRegistry,
|
||||
license,
|
||||
},
|
||||
createPackageVersionProvider(lockfile),
|
||||
isMonoRepo,
|
||||
);
|
||||
|
||||
Task.section('Moving to final location');
|
||||
await movePlugin(tempDir, pluginDir, pluginId);
|
||||
|
||||
Task.section('Building the plugin');
|
||||
await buildPlugin(pluginDir);
|
||||
|
||||
if ((await fs.pathExists(appPackage)) && !opts.backend) {
|
||||
Task.section('Adding plugin as dependency in app');
|
||||
await addPluginDependencyToApp(paths.targetRoot, name, pluginVersion);
|
||||
|
||||
Task.section('Import plugin in app');
|
||||
await addPluginExtensionToApp(pluginId, extensionName, name);
|
||||
}
|
||||
|
||||
if (answers.owner) {
|
||||
await addCodeownersEntry(`/plugins/${pluginId}`, answers.owner);
|
||||
}
|
||||
|
||||
Task.log();
|
||||
Task.log(`🥇 Successfully created ${chalk.cyan(`${name}`)}`);
|
||||
Task.log();
|
||||
Task.exit();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
Task.error(error.message);
|
||||
|
||||
Task.log('It seems that something went wrong when creating the plugin 🤔');
|
||||
Task.log('We are going to clean up, and then you can try again.');
|
||||
|
||||
Task.section('Cleanup');
|
||||
await cleanUp(tempDir);
|
||||
Task.error('🔥 Failed to create plugin!');
|
||||
Task.exit(1);
|
||||
}
|
||||
};
|
||||
@@ -265,63 +265,6 @@ export function registerCommands(program: Command) {
|
||||
.option('--no-private', 'Do not mark new packages as private')
|
||||
.action(lazy(() => import('./new/new').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('create', { hidden: true })
|
||||
.storeOptionsAsProperties(false)
|
||||
.description(
|
||||
'Open up an interactive guide to creating new things in your app [DEPRECATED]',
|
||||
)
|
||||
.option(
|
||||
'--select <name>',
|
||||
'Select the thing you want to be creating upfront',
|
||||
)
|
||||
.option(
|
||||
'--option <name>=<value>',
|
||||
'Pre-fill options for the creation process',
|
||||
(opt, arr: string[]) => [...arr, opt],
|
||||
[],
|
||||
)
|
||||
.option('--scope <scope>', 'The scope to use for new packages')
|
||||
.option(
|
||||
'--npm-registry <URL>',
|
||||
'The package registry to use for new packages',
|
||||
)
|
||||
.option('--no-private', 'Do not mark new packages as private')
|
||||
.action(lazy(() => import('./new/new').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('create-plugin', { hidden: true })
|
||||
.option(
|
||||
'--backend',
|
||||
'Create plugin with the backend dependencies as default',
|
||||
)
|
||||
.description('Creates a new plugin in the current repository [DEPRECATED]')
|
||||
.option('--scope <scope>', 'npm scope')
|
||||
.option('--npm-registry <URL>', 'npm registry URL')
|
||||
.option('--no-private', 'Public npm package')
|
||||
.action(
|
||||
lazy(() => import('./create-plugin/createPlugin').then(m => m.default)),
|
||||
);
|
||||
|
||||
program
|
||||
.command('plugin:diff', { hidden: true })
|
||||
.option('--check', 'Fail if changes are required')
|
||||
.option('--yes', 'Apply all changes')
|
||||
.description(
|
||||
'Diff an existing plugin with the creation template [DEPRECATED]',
|
||||
)
|
||||
.action(lazy(() => import('./plugin/diff').then(m => m.default)));
|
||||
|
||||
// TODO(Rugvip): Deprecate in favor of package variant
|
||||
program
|
||||
.command('test')
|
||||
.allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args
|
||||
.helpOption(', --backstage-cli-help') // Let Jest handle help
|
||||
.description(
|
||||
'Run tests, forwarding args to Jest, defaulting to watch mode [DEPRECATED]',
|
||||
)
|
||||
.action(lazy(() => import('./test').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('config:docs')
|
||||
.option(
|
||||
@@ -403,12 +346,6 @@ export function registerCommands(program: Command) {
|
||||
.description('Bump Backstage packages to the latest versions')
|
||||
.action(lazy(() => import('./versions/bump').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('versions:check', { hidden: true })
|
||||
.option('--fix', 'Fix any auto-fixable versioning problems')
|
||||
.description('Check Backstage package versioning')
|
||||
.action(lazy(() => import('./versions/lint').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('versions:migrate')
|
||||
.option(
|
||||
@@ -424,12 +361,6 @@ export function registerCommands(program: Command) {
|
||||
)
|
||||
.action(lazy(() => import('./versions/migrate').then(m => m.default)));
|
||||
|
||||
// TODO(Rugvip): Deprecate in favor of package variant
|
||||
program
|
||||
.command('clean')
|
||||
.description('Delete cache directories [DEPRECATED]')
|
||||
.action(lazy(() => import('./clean/clean').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('build-workspace <workspace-dir> [packages...]')
|
||||
.option(
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { OptionValues } from 'commander';
|
||||
import {
|
||||
diffTemplateFiles,
|
||||
handlers,
|
||||
handleAllFiles,
|
||||
inquirerPromptFunc,
|
||||
makeCheckPromptFunc,
|
||||
yesPromptFunc,
|
||||
} from '../../lib/diff';
|
||||
import { paths } from '../../lib/paths';
|
||||
|
||||
export type PluginData = {
|
||||
id: string;
|
||||
name: string;
|
||||
privatePackage: string;
|
||||
pluginVersion: string;
|
||||
npmRegistry: string;
|
||||
};
|
||||
|
||||
const fileHandlers = [
|
||||
{
|
||||
patterns: ['package.json'],
|
||||
handler: handlers.packageJson,
|
||||
},
|
||||
{
|
||||
// make sure files in 1st level of src/ and dev/ exist
|
||||
patterns: ['.eslintrc.js'],
|
||||
handler: handlers.exists,
|
||||
},
|
||||
{
|
||||
patterns: ['README.md', 'tsconfig.json', /^src\//, /^dev\//],
|
||||
handler: handlers.skip,
|
||||
},
|
||||
];
|
||||
|
||||
export default async (opts: OptionValues) => {
|
||||
let promptFunc = inquirerPromptFunc;
|
||||
let finalize = () => {};
|
||||
|
||||
if (opts.check) {
|
||||
[promptFunc, finalize] = makeCheckPromptFunc();
|
||||
} else if (opts.yes) {
|
||||
promptFunc = yesPromptFunc;
|
||||
}
|
||||
|
||||
const data = await readPluginData();
|
||||
const templateFiles = await diffTemplateFiles('default-plugin', data);
|
||||
await handleAllFiles(fileHandlers, templateFiles, promptFunc);
|
||||
finalize();
|
||||
};
|
||||
|
||||
// Reads templating data from the existing plugin
|
||||
async function readPluginData(): Promise<PluginData> {
|
||||
let name: string;
|
||||
let privatePackage: string;
|
||||
let pluginVersion: string;
|
||||
let npmRegistry: string;
|
||||
try {
|
||||
const pkg = require(paths.resolveTarget('package.json'));
|
||||
name = pkg.name;
|
||||
privatePackage = pkg.private;
|
||||
pluginVersion = 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}`);
|
||||
}
|
||||
|
||||
const pluginTsContents = await fs.readFile(
|
||||
paths.resolveTarget('src/plugin.ts'),
|
||||
'utf8',
|
||||
);
|
||||
// TODO: replace with some proper parsing logic or plugin metadata file
|
||||
const pluginIdMatch = pluginTsContents.match(/id: ['"`](.+?)['"`]/);
|
||||
if (!pluginIdMatch) {
|
||||
throw new Error(`Failed to parse plugin.ts, no plugin ID found`);
|
||||
}
|
||||
|
||||
const id = pluginIdMatch[1];
|
||||
|
||||
return { id, name, privatePackage, pluginVersion, npmRegistry };
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { OptionValues } from 'commander';
|
||||
import { run } from '../../lib/run';
|
||||
|
||||
export default async (opts: OptionValues) => {
|
||||
const args = ['test'];
|
||||
|
||||
if (opts.watch) {
|
||||
args.push('--watch');
|
||||
}
|
||||
if (opts.coverage) {
|
||||
args.push('--coverage');
|
||||
}
|
||||
|
||||
await run('web-scripts', args);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export const pluginsFileContent = `
|
||||
export { plugin as WelcomePlugin } from '@backstage/plugin-welcome';
|
||||
export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse';`;
|
||||
|
||||
export const codeownersFileContent = `
|
||||
* @spotify/backstage-core
|
||||
/docs/features/techdocs @spotify/techdocs-core
|
||||
/plugins/cost-insights @spotify/silver-lining
|
||||
`;
|
||||
|
||||
export const packageFileContent = {
|
||||
name: 'example-app',
|
||||
version: '0.1.1',
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
scripts: {},
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default async () => {
|
||||
throw new Error(
|
||||
'This command has been removed, please consider alternatives such as `yarn dedupe` instead.',
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user