Merge branch 'backstage:master' into master
This commit is contained in:
+25
-29
@@ -12,8 +12,7 @@ Options:
|
||||
-h, --help
|
||||
|
||||
Commands:
|
||||
create [options]
|
||||
create-plugin [options]
|
||||
new [options]
|
||||
plugin:diff [options]
|
||||
test
|
||||
config:docs [options]
|
||||
@@ -100,20 +99,6 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli create`
|
||||
|
||||
```
|
||||
Usage: backstage-cli create [options]
|
||||
|
||||
Options:
|
||||
--select <name>
|
||||
--option <name>=<value>
|
||||
--scope <scope>
|
||||
--npm-registry <URL>
|
||||
--no-private
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli create-github-app`
|
||||
|
||||
```
|
||||
@@ -123,19 +108,6 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli create-plugin`
|
||||
|
||||
```
|
||||
Usage: backstage-cli create-plugin [options]
|
||||
|
||||
Options:
|
||||
--backend
|
||||
--scope <scope>
|
||||
--npm-registry <URL>
|
||||
--no-private
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli info`
|
||||
|
||||
```
|
||||
@@ -197,6 +169,20 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli new`
|
||||
|
||||
```
|
||||
Usage: backstage-cli new [options]
|
||||
|
||||
Options:
|
||||
--select <name>
|
||||
--option <name>=<value>
|
||||
--scope <scope>
|
||||
--npm-registry <URL>
|
||||
--no-private
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli package`
|
||||
|
||||
```
|
||||
@@ -415,6 +401,7 @@ Options:
|
||||
Commands:
|
||||
build [options]
|
||||
lint [options]
|
||||
clean
|
||||
help [command]
|
||||
```
|
||||
|
||||
@@ -429,6 +416,15 @@ Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli repo clean`
|
||||
|
||||
```
|
||||
Usage: backstage-cli repo clean [options]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
```
|
||||
|
||||
### `backstage-cli repo lint`
|
||||
|
||||
```
|
||||
|
||||
@@ -248,6 +248,32 @@
|
||||
"description": "The port that the frontend should be bound to. Only used for local development."
|
||||
}
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"type": "object",
|
||||
"description": "Only used for local development. The https object is passed to webpack in order to enable using https on localhost.",
|
||||
"properties": {
|
||||
"certificate": {
|
||||
"type": "object",
|
||||
"description": "Parent object containing certificate and the private key",
|
||||
"required": [
|
||||
"key",
|
||||
"cert"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"visibility": "secret",
|
||||
"description": "Https Certificate private key. Use $file to load in a file"
|
||||
},
|
||||
"cert": {
|
||||
"type": "string",
|
||||
"visibility": "secret",
|
||||
"description": "Https Certificate. Use $file to load in a file"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ export function registerRepoCommand(program: Command) {
|
||||
.option('--fix', 'Attempt to automatically fix violations')
|
||||
.action(lazy(() => import('./repo/lint').then(m => m.command)));
|
||||
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache and output directories')
|
||||
.action(lazy(() => import('./repo/clean').then(m => m.command)));
|
||||
|
||||
command
|
||||
.command('list-deprecations', { hidden: true })
|
||||
.description('List deprecations. [EXPERIMENTAL]')
|
||||
@@ -193,7 +198,7 @@ export function registerMigrateCommand(program: Command) {
|
||||
|
||||
export function registerCommands(program: Command) {
|
||||
program
|
||||
.command('create')
|
||||
.command('new')
|
||||
.storeOptionsAsProperties(false)
|
||||
.description(
|
||||
'Open up an interactive guide to creating new things in your app',
|
||||
@@ -214,15 +219,39 @@ export function registerCommands(program: Command) {
|
||||
'The package registry to use for new packages',
|
||||
)
|
||||
.option('--no-private', 'Do not mark new packages as private')
|
||||
.action(lazy(() => import('./create/create').then(m => m.default)));
|
||||
.action(lazy(() => import('./new/new').then(m => m.default)));
|
||||
|
||||
program
|
||||
.command('create-plugin')
|
||||
.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')
|
||||
.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')
|
||||
|
||||
@@ -18,7 +18,7 @@ import os from 'os';
|
||||
import fs from 'fs-extra';
|
||||
import { join as joinPath } from 'path';
|
||||
import { OptionValues } from 'commander';
|
||||
import { FactoryRegistry } from '../../lib/create/FactoryRegistry';
|
||||
import { FactoryRegistry } from '../../lib/new/FactoryRegistry';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { Task } from '../../lib/tasks';
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 { execFile as execFileCb } from 'child_process';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { PackageGraph } from '../../lib/monorepo';
|
||||
import { paths } from '../../lib/paths';
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
|
||||
export async function command(): Promise<void> {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
|
||||
await fs.remove(paths.resolveTargetRoot('dist'));
|
||||
await fs.remove(paths.resolveTargetRoot('dist-types'));
|
||||
await fs.remove(paths.resolveTargetRoot('coverage'));
|
||||
|
||||
await Promise.all(
|
||||
Array.from(Array(10), async () => {
|
||||
while (packages.length > 0) {
|
||||
const pkg = packages.pop()!;
|
||||
const cleanScript = pkg.packageJson.scripts?.clean;
|
||||
|
||||
if (
|
||||
cleanScript === 'backstage-cli clean' ||
|
||||
cleanScript === 'backstage-cli package clean'
|
||||
) {
|
||||
await fs.remove(resolvePath(pkg.dir, 'dist'));
|
||||
await fs.remove(resolvePath(pkg.dir, 'dist-types'));
|
||||
await fs.remove(resolvePath(pkg.dir, 'coverage'));
|
||||
} else if (cleanScript) {
|
||||
const result = await execFile('yarn', ['run', 'clean'], {
|
||||
cwd: pkg.dir,
|
||||
shell: true,
|
||||
});
|
||||
process.stdout.write(result.stdout);
|
||||
process.stderr.write(result.stderr);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -174,6 +174,8 @@ describe('bump', () => {
|
||||
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
|
||||
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
|
||||
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'Running yarn install to install new versions',
|
||||
'⚠️ The following packages may have breaking changes:',
|
||||
@@ -210,15 +212,15 @@ describe('bump', () => {
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5', // not bumped since new version is within range
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3', // not bumped
|
||||
'@backstage/theme': '^2.0.0', // bumped since newer
|
||||
'@backstage/core': '^1.0.6',
|
||||
'@backstage/theme': '^2.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -297,6 +299,8 @@ describe('bump', () => {
|
||||
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
|
||||
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
|
||||
'bumping @backstage/theme in b to ^5.0.0',
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'Your project is now at version 0.0.1, which has been written to backstage.json',
|
||||
'Running yarn install to install new versions',
|
||||
'⚠️ The following packages may have breaking changes:',
|
||||
@@ -333,15 +337,15 @@ describe('bump', () => {
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5', // not bumped since new version is within range
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3', // not bumped
|
||||
'@backstage/theme': '^5.0.0', // bumped since newer
|
||||
'@backstage/core': '^1.0.6',
|
||||
'@backstage/theme': '^5.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -544,6 +548,8 @@ describe('bump', () => {
|
||||
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
|
||||
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
|
||||
'bumping @backstage/theme in b to ^5.0.0',
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'Your project is now at version 1.0.0, which has been written to backstage.json',
|
||||
'Running yarn install to install new versions',
|
||||
'⚠️ The following packages may have breaking changes:',
|
||||
@@ -650,7 +656,11 @@ describe('bump', () => {
|
||||
'unlocking @backstage-extra/custom@^1.0.1 ~> 1.1.0',
|
||||
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
|
||||
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'bumping @backstage-extra/custom in a to ^1.1.0',
|
||||
'bumping @backstage-extra/custom-two in a to ^2.0.0',
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage-extra/custom in b to ^1.1.0',
|
||||
'bumping @backstage-extra/custom-two in b to ^2.0.0',
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'Skipping backstage.json update as custom pattern is used',
|
||||
@@ -690,9 +700,9 @@ describe('bump', () => {
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage-extra/custom': '^1.0.1',
|
||||
'@backstage-extra/custom': '^1.1.0',
|
||||
'@backstage-extra/custom-two': '^2.0.0',
|
||||
'@backstage/core': '^1.0.5', // not bumped since new version is within range
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
@@ -701,8 +711,8 @@ describe('bump', () => {
|
||||
dependencies: {
|
||||
'@backstage-extra/custom': '^1.1.0',
|
||||
'@backstage-extra/custom-two': '^2.0.0',
|
||||
'@backstage/core': '^1.0.3', // not bumped
|
||||
'@backstage/theme': '^2.0.0', // bumped since newer
|
||||
'@backstage/core': '^1.0.6',
|
||||
'@backstage/theme': '^2.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,13 +124,6 @@ export default async (opts: OptionValues) => {
|
||||
}
|
||||
|
||||
for (const pkg of pkgs) {
|
||||
if (semver.satisfies(target, pkg.range)) {
|
||||
if (semver.minVersion(pkg.range)?.version !== target) {
|
||||
unlocked.push({ name, range: pkg.range, target });
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
versionBumps.set(
|
||||
pkg.name,
|
||||
(versionBumps.get(pkg.name) ?? []).concat({
|
||||
|
||||
@@ -40,6 +40,7 @@ export async function serveBundle(options: ServeOptions) {
|
||||
isDev: true,
|
||||
baseUrl: url,
|
||||
});
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
const server = new WebpackDevServer(
|
||||
@@ -60,7 +61,15 @@ export async function serveBundle(options: ServeOptions) {
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
},
|
||||
https: url.protocol === 'https:',
|
||||
https:
|
||||
url.protocol === 'https:'
|
||||
? {
|
||||
cert: options.backendConfig.getString(
|
||||
'app.https.certificate.cert',
|
||||
),
|
||||
key: options.backendConfig.getString('app.https.certificate.key'),
|
||||
}
|
||||
: false,
|
||||
host,
|
||||
port,
|
||||
proxy: pkg.proxy,
|
||||
|
||||
@@ -31,6 +31,7 @@ export type ServeOptions = BundlingPathsOptions & {
|
||||
checksEnabled: boolean;
|
||||
frontendConfig: Config;
|
||||
frontendAppConfigs: AppConfig[];
|
||||
backendConfig: Config;
|
||||
};
|
||||
|
||||
export type BuildOptions = BundlingPathsOptions & {
|
||||
|
||||
@@ -96,11 +96,15 @@ export async function loadCliConfig(options: Options) {
|
||||
});
|
||||
const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs);
|
||||
|
||||
const backendAppConfigs = schema.process(appConfigs);
|
||||
const backendConfig = ConfigReader.fromConfigs(backendAppConfigs);
|
||||
|
||||
return {
|
||||
schema,
|
||||
appConfigs,
|
||||
frontendConfig,
|
||||
frontendAppConfigs,
|
||||
backendConfig,
|
||||
};
|
||||
} catch (error) {
|
||||
const maybeSchemaError = error as Error & { messages?: string[] };
|
||||
|
||||
@@ -453,18 +453,19 @@ describe('discovery', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw elements within element prop contains a path', () => {
|
||||
expect(() => {
|
||||
traverseElementTree({
|
||||
root: <Route path="foo" element={<Extension3 path="bar" />} />,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routing: routingV2Collector,
|
||||
},
|
||||
});
|
||||
}).toThrow(
|
||||
'Elements within the element prop tree may not have paths, found "bar"',
|
||||
);
|
||||
it('should ignore path props within route elements', () => {
|
||||
const { routing } = traverseElementTree({
|
||||
root: <Route path="foo" element={<Extension1 path="bar" />} />,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routing: routingV2Collector,
|
||||
},
|
||||
});
|
||||
expect(sortedEntries(routing.paths)).toEqual([[ref1, 'foo']]);
|
||||
expect(sortedEntries(routing.parents)).toEqual([[ref1, undefined]]);
|
||||
expect(routing.objects).toEqual([
|
||||
routeObj('foo', [ref1], [], undefined, plugin),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw when a routable extension does not have a path set', () => {
|
||||
|
||||
@@ -53,6 +53,8 @@ interface RoutingV2CollectorContext {
|
||||
isElementAncestor?: boolean;
|
||||
}
|
||||
|
||||
// This collects all the mount points and their plugins within an element tree.
|
||||
// Unlike regular traversal this ignores all other things, like path props and mount point gatherers.
|
||||
function collectSubTree(
|
||||
node: ReactNode,
|
||||
entries = new Array<{ routeRef: RouteRef; plugin?: BackstagePlugin }>(),
|
||||
@@ -62,12 +64,6 @@ function collectSubTree(
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.props.path) {
|
||||
throw new Error(
|
||||
`Elements within the element prop tree may not have paths, found "${element.props.path}"`,
|
||||
);
|
||||
}
|
||||
|
||||
const routeRef = getComponentData<RouteRef>(element, 'core.mountPoint');
|
||||
if (routeRef) {
|
||||
const plugin = getComponentData<BackstagePlugin>(element, 'core.plugin');
|
||||
@@ -87,6 +83,16 @@ export const routingV2Collector = createCollector(
|
||||
objects: new Array<BackstageRouteObject>(),
|
||||
}),
|
||||
(acc, node, parent, ctx?: RoutingV2CollectorContext) => {
|
||||
// If we're in an element prop, ignore everything
|
||||
if (ctx?.isElementAncestor) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// Start ignoring everything if we enter an element prop
|
||||
if (parent?.props.element === node) {
|
||||
return { ...ctx, isElementAncestor: true };
|
||||
}
|
||||
|
||||
const pathProp: unknown = node.props?.path;
|
||||
|
||||
const mountPoint = getComponentData<RouteRef>(node, 'core.mountPoint');
|
||||
@@ -98,15 +104,6 @@ export const routingV2Collector = createCollector(
|
||||
);
|
||||
}
|
||||
|
||||
// If we're in an element prop, ignore everything
|
||||
if (ctx?.isElementAncestor) {
|
||||
return ctx;
|
||||
}
|
||||
// Start ignoring everything if we enter an element prop
|
||||
if (parent?.props.element === node) {
|
||||
return { ...ctx, isElementAncestor: true };
|
||||
}
|
||||
|
||||
const parentChildren = ctx?.obj?.children ?? acc.objects;
|
||||
|
||||
if (pathProp !== undefined) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"build-image": "yarn workspace backend build-image",
|
||||
"tsc": "tsc",
|
||||
"tsc:full": "tsc --skipLibCheck false --incremental false",
|
||||
"clean": "backstage-cli clean && lerna run clean",
|
||||
"clean": "backstage-cli repo clean",
|
||||
"diff": "lerna run diff --",
|
||||
"test": "backstage-cli test",
|
||||
"test:all": "lerna run test -- --coverage",
|
||||
@@ -21,7 +21,7 @@
|
||||
"lint:all": "backstage-cli repo lint",
|
||||
"prettier:check": "prettier --check .",
|
||||
"create-plugin": "backstage-cli create-plugin --scope internal",
|
||||
"remove-plugin": "backstage-cli remove-plugin"
|
||||
"new": "backstage-cli new --scope internal"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
||||
Reference in New Issue
Block a user