Merge branch 'master' into add-git-force
Signed-off-by: Niya Ma <78030299+niya-ma-1@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Improve `scm/git` wrapper around `isomorphic-git` library :
|
||||
|
||||
- Add `checkout` function,
|
||||
- Add optional `remoteRef` parameter in the `push` function.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Introduced a new experimental feature that allows you to declare plugin-wide options for your plugin by defining
|
||||
`__experimentalConfigure` in your `createPlugin` options. See https://backstage.io/docs/plugins/customization.md for more information.
|
||||
|
||||
This is an experimental feature and it will have breaking changes in the future.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Add a `publish:gerrit:review` scaffolder action
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
Plugin catalog has been modified to use an experimental feature where you can customize the title of the create button.
|
||||
|
||||
You can modify it by doing:
|
||||
|
||||
```typescript jsx
|
||||
import { catalogPlugin } from '@backstage/plugin-catalog';
|
||||
|
||||
catalogPlugin.__experimentalReconfigure({
|
||||
createButtonTitle: 'New',
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Updated dependencies `@svgr/*` to `6.3.x`.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/backend-tasks': patch
|
||||
'@backstage/integration': patch
|
||||
'@backstage/plugin-cicd-statistics': patch
|
||||
'@backstage/plugin-code-climate': patch
|
||||
'@backstage/plugin-gocd': patch
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
'@backstage/plugin-periskop': patch
|
||||
'@backstage/plugin-sentry': patch
|
||||
'@backstage/plugin-splunk-on-call': patch
|
||||
'@backstage/plugin-tech-insights-common': patch
|
||||
'@backstage/plugin-tech-insights-node': patch
|
||||
'@backstage/plugin-xcmetrics': patch
|
||||
---
|
||||
|
||||
Updated dependency `@types/luxon` to `^3.0.0`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
Updated `publish:gitlab:merge-request` action to allow commit updates and deletes
|
||||
+21
-21
@@ -40,14 +40,14 @@
|
||||
lodash.once "^4.1.1"
|
||||
|
||||
"@types/node@*":
|
||||
version "18.0.0"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
|
||||
integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
|
||||
version "18.0.6"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7"
|
||||
integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==
|
||||
|
||||
"@types/node@^14.14.31":
|
||||
version "14.18.21"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41"
|
||||
integrity sha512-x5W9s+8P4XteaxT/jKF0PSb7XEvo5VmqEWgsMlyeY4ZlLK8I6aH6g5TPPyDlLAep+GYf4kefb7HFyc7PAO3m+Q==
|
||||
version "14.18.22"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-14.18.22.tgz#fd2a15dca290fc9ad565b672fde746191cd0c6e6"
|
||||
integrity sha512-qzaYbXVzin6EPjghf/hTdIbnVW1ErMx8rPzwRNJhlbyJhu2SyqlvjGOY/tbUt6VFyzg56lROcOeSQRInpt63Yw==
|
||||
|
||||
"@types/sinonjs__fake-timers@8.1.1":
|
||||
version "8.1.1"
|
||||
@@ -304,9 +304,9 @@ cross-spawn@^7.0.0:
|
||||
which "^2.0.1"
|
||||
|
||||
cypress@^10.0.0:
|
||||
version "10.3.0"
|
||||
resolved "https://registry.npmjs.org/cypress/-/cypress-10.3.0.tgz#fae8d32f0822fcfb938e79c7c31ef344794336ae"
|
||||
integrity sha512-txkQWKzvBVnWdCuKs5Xc08gjpO89W2Dom2wpZgT9zWZT5jXxqPIxqP/NC1YArtkpmp3fN5HW8aDjYBizHLUFvg==
|
||||
version "10.3.1"
|
||||
resolved "https://registry.npmjs.org/cypress/-/cypress-10.3.1.tgz#7fab4ef43481c05a9a17ebe9a0ec860e15b95a19"
|
||||
integrity sha512-As9HrExjAgpgjCnbiQCuPdw5sWKx5HUJcK2EOKziu642akwufr/GUeqL5UnCPYXTyyibvEdWT/pSC2qnGW/e5w==
|
||||
dependencies:
|
||||
"@cypress/request" "^2.88.10"
|
||||
"@cypress/xvfb" "^1.2.4"
|
||||
@@ -359,9 +359,9 @@ dashdash@^1.12.0:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
dayjs@^1.10.4:
|
||||
version "1.11.3"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
|
||||
integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
|
||||
version "1.11.4"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e"
|
||||
integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.7"
|
||||
@@ -415,9 +415,9 @@ escape-string-regexp@^1.0.5:
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
eventemitter2@^6.4.3:
|
||||
version "6.4.5"
|
||||
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655"
|
||||
integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==
|
||||
version "6.4.6"
|
||||
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.6.tgz#92d56569cc147a4d9b9da9e942e89b20ce236b0a"
|
||||
integrity sha512-OHqo4wbHX5VbvlbB6o6eDwhYmiTjrpWACjF8Pmof/GTD6rdBNdZFNck3xlhqOiQFGCOoq3uzHvA0cQpFHIGVAQ==
|
||||
|
||||
execa@4.1.0:
|
||||
version "4.1.0"
|
||||
@@ -853,9 +853,9 @@ proxy-from-env@1.0.0:
|
||||
integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
|
||||
|
||||
psl@^1.1.28:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
|
||||
version "1.9.0"
|
||||
resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
@@ -903,9 +903,9 @@ rimraf@^3.0.0:
|
||||
glob "^7.1.3"
|
||||
|
||||
rxjs@^7.5.1:
|
||||
version "7.5.5"
|
||||
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f"
|
||||
integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
|
||||
version "7.5.6"
|
||||
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc"
|
||||
integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
|
||||
@@ -62,13 +62,16 @@ add the following:
|
||||
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
catalogApi,
|
||||
}).build();
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: customization
|
||||
title: Customization
|
||||
description: Documentation on adding a customization logic to the plugin
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Backstage core logic provides a possibility to make the component customizable in such a way that the application
|
||||
developer can redefine the labels, icons, elements or even completely replace the component. It's up to each plugin
|
||||
to decide what can be customized.
|
||||
|
||||
## For a plugin developer
|
||||
|
||||
When you are creating your plugin, you have a possibility to use a metadata field and define there all
|
||||
customizable elements. For example
|
||||
|
||||
```typescript jsx
|
||||
const plugin = createPlugin({
|
||||
id: 'my-plugin',
|
||||
__experimentalConfigure(
|
||||
options?: CatalogInputPluginOptions,
|
||||
): CatalogPluginOptions {
|
||||
const defaultOptions = {
|
||||
createButtonTitle: 'Create',
|
||||
};
|
||||
return { ...defaultOptions, ...options };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
And the rendering part of the exposed component can retrieve that metadata as:
|
||||
|
||||
```typescript jsx
|
||||
export type CatalogPluginOptions = {
|
||||
createButtonTitle: string;
|
||||
};
|
||||
|
||||
export type CatalogInputPluginOptions = {
|
||||
createButtonTitle: string;
|
||||
};
|
||||
|
||||
export const useCatalogPluginOptions = () =>
|
||||
usePluginOptions<CatalogPluginOptions>();
|
||||
|
||||
export function DefaultMyPluginWelcomePage() {
|
||||
const { createButtonTitle } = useCatalogPluginOptions();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button>{createButtonTitle}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## For an application developer using the plugin
|
||||
|
||||
The way to reconfigure the default values provided by the plugin you can do it via reconfigure method, defined on the
|
||||
plugin. Example:
|
||||
|
||||
```typescript jsx
|
||||
import { myPlugin } from '@backstage/my-plugin';
|
||||
|
||||
myPlugin.__experimentalReconfigure({
|
||||
createButtonTitle: 'New',
|
||||
});
|
||||
```
|
||||
@@ -20,7 +20,6 @@
|
||||
"@backstage/plugin-azure-devops": "^0.1.23",
|
||||
"@backstage/plugin-apache-airflow": "^0.2.0",
|
||||
"@backstage/plugin-badges": "^0.2.31",
|
||||
"@backstage/plugin-catalog": "^1.4.0",
|
||||
"@backstage/plugin-catalog-common": "^1.0.4",
|
||||
"@backstage/plugin-catalog-graph": "^0.2.19",
|
||||
"@backstage/plugin-catalog-import": "^0.8.10",
|
||||
@@ -70,6 +69,7 @@
|
||||
"@roadiehq/backstage-plugin-github-insights": "^2.0.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^2.0.0",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^2.0.0",
|
||||
"@internal/plugin-catalog-customized": "0.0.0",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -35,11 +35,13 @@ import {
|
||||
} from '@backstage/core-components';
|
||||
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
|
||||
import { AzurePullRequestsPage } from '@backstage/plugin-azure-devops';
|
||||
|
||||
import {
|
||||
CatalogEntityPage,
|
||||
CatalogIndexPage,
|
||||
catalogPlugin,
|
||||
} from '@backstage/plugin-catalog';
|
||||
} from '@internal/plugin-catalog-customized';
|
||||
|
||||
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
|
||||
import {
|
||||
CatalogImportPage,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EntityLayout } from '@backstage/plugin-catalog';
|
||||
import { EntityLayout } from '@internal/plugin-catalog-customized';
|
||||
import {
|
||||
EntityProvider,
|
||||
starredEntitiesApiRef,
|
||||
|
||||
@@ -59,7 +59,7 @@ import {
|
||||
isComponentType,
|
||||
isKind,
|
||||
isOrphan,
|
||||
} from '@backstage/plugin-catalog';
|
||||
} from '@internal/plugin-catalog-customized';
|
||||
import {
|
||||
Direction,
|
||||
EntityCatalogGraphCard,
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
useContent,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
import {
|
||||
catalogApiRef,
|
||||
CATALOG_FILTER_EXISTS,
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
useSidebarPinState,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
import {
|
||||
catalogApiRef,
|
||||
CATALOG_FILTER_EXISTS,
|
||||
|
||||
@@ -340,6 +340,8 @@ export class Git {
|
||||
url: string;
|
||||
force?: boolean;
|
||||
}): Promise<void>;
|
||||
// (undocumented)
|
||||
checkout(options: { dir: string; ref: string }): Promise<void>;
|
||||
clone(options: {
|
||||
url: string;
|
||||
dir: string;
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"archiver": "^5.0.2",
|
||||
"aws-sdk": "^2.840.0",
|
||||
|
||||
@@ -78,6 +78,22 @@ describe('Git', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkout', () => {
|
||||
it('should call isomorphic-git with the correct arguments', async () => {
|
||||
const git = Git.fromAuth({});
|
||||
const dir = 'mockdirectory';
|
||||
const ref = 'master';
|
||||
|
||||
await git.checkout({ dir, ref });
|
||||
|
||||
expect(isomorphic.checkout).toHaveBeenCalledWith({
|
||||
fs,
|
||||
dir,
|
||||
ref,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('commit', () => {
|
||||
it('should call isomorphic-git with the correct arguments', async () => {
|
||||
const git = Git.fromAuth({});
|
||||
@@ -332,6 +348,31 @@ describe('Git', () => {
|
||||
onAuth: expect.any(Function),
|
||||
});
|
||||
});
|
||||
it('should call isomorphic-git with remoteRef parameter', async () => {
|
||||
const remote = 'origin';
|
||||
const remoteRef = 'refs/for/master';
|
||||
const dir = '/some/mock/dir';
|
||||
const auth = {
|
||||
username: 'blob',
|
||||
password: 'hunter2',
|
||||
};
|
||||
const git = Git.fromAuth(auth);
|
||||
|
||||
await git.push({ dir, remote, remoteRef });
|
||||
|
||||
expect(isomorphic.push).toHaveBeenCalledWith({
|
||||
fs,
|
||||
http,
|
||||
remote,
|
||||
remoteRef,
|
||||
dir,
|
||||
onProgress: expect.any(Function),
|
||||
headers: {
|
||||
'user-agent': 'git/@isomorphic-git',
|
||||
},
|
||||
onAuth: expect.any(Function),
|
||||
});
|
||||
});
|
||||
it('should pass a function that returns the authorization as the onAuth handler', async () => {
|
||||
const remote = 'origin';
|
||||
const dir = '/some/mock/dir';
|
||||
|
||||
@@ -73,6 +73,13 @@ export class Git {
|
||||
return git.deleteRemote({ fs, dir, remote });
|
||||
}
|
||||
|
||||
async checkout(options: { dir: string; ref: string }): Promise<void> {
|
||||
const { dir, ref } = options;
|
||||
this.config.logger?.info(`Checking out branch {dir=${dir},ref=${ref}}`);
|
||||
|
||||
return git.checkout({ fs, dir, ref });
|
||||
}
|
||||
|
||||
async commit(options: {
|
||||
dir: string;
|
||||
message: string;
|
||||
@@ -219,6 +226,7 @@ export class Git {
|
||||
'user-agent': 'git/@isomorphic-git',
|
||||
},
|
||||
remote: remote,
|
||||
remoteRef: remoteRef,
|
||||
onAuth: this.onAuth,
|
||||
});
|
||||
} catch (ex) {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@backstage/config": "^1.0.1",
|
||||
"@backstage/errors": "^1.1.0",
|
||||
"@backstage/types": "^1.0.0",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"cron": "^2.0.0",
|
||||
"knex": "^2.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
"@types/luxon": "^2.0.4"
|
||||
"@types/luxon": "^3.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -50,10 +50,10 @@
|
||||
"@spotify/eslint-config-typescript": "^14.0.0",
|
||||
"@sucrase/jest-plugin": "^2.1.1",
|
||||
"@sucrase/webpack-loader": "^2.0.0",
|
||||
"@svgr/plugin-jsx": "6.2.x",
|
||||
"@svgr/plugin-svgo": "6.2.x",
|
||||
"@svgr/rollup": "6.2.x",
|
||||
"@svgr/webpack": "6.2.x",
|
||||
"@svgr/plugin-jsx": "6.3.x",
|
||||
"@svgr/plugin-svgo": "6.3.x",
|
||||
"@svgr/rollup": "6.3.x",
|
||||
"@svgr/webpack": "6.3.x",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
||||
"@typescript-eslint/parser": "^5.9.0",
|
||||
|
||||
@@ -214,6 +214,7 @@ export type BackstageIdentityResponse = {
|
||||
export type BackstagePlugin<
|
||||
Routes extends AnyRoutes = {},
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
PluginInputOptions extends {} = {},
|
||||
> = {
|
||||
getId(): string;
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
@@ -221,6 +222,7 @@ export type BackstagePlugin<
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
__experimentalReconfigure(options: PluginInputOptions): void;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -303,9 +305,10 @@ export function createExternalRouteRef<
|
||||
export function createPlugin<
|
||||
Routes extends AnyRoutes = {},
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
PluginInputOptions extends {} = {},
|
||||
>(
|
||||
config: PluginConfig<Routes, ExternalRoutes>,
|
||||
): BackstagePlugin<Routes, ExternalRoutes>;
|
||||
config: PluginConfig<Routes, ExternalRoutes, PluginInputOptions>,
|
||||
): BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions>;
|
||||
|
||||
// @public
|
||||
export function createReactExtension<
|
||||
@@ -621,12 +624,14 @@ export type PendingOAuthRequest = {
|
||||
export type PluginConfig<
|
||||
Routes extends AnyRoutes,
|
||||
ExternalRoutes extends AnyExternalRoutes,
|
||||
PluginInputOptions extends {},
|
||||
> = {
|
||||
id: string;
|
||||
apis?: Iterable<AnyApiFactory>;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
__experimentalConfigure?(options?: PluginInputOptions): {};
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -634,6 +639,20 @@ export type PluginFeatureFlagConfig = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export interface PluginOptionsProviderProps {
|
||||
// (undocumented)
|
||||
children: ReactNode;
|
||||
// (undocumented)
|
||||
plugin?: BackstagePlugin;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export const PluginProvider: ({
|
||||
children,
|
||||
plugin,
|
||||
}: PluginOptionsProviderProps) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export type ProfileInfo = {
|
||||
email?: string;
|
||||
@@ -734,6 +753,11 @@ export function useElementFilter<T>(
|
||||
dependencies?: any[],
|
||||
): T;
|
||||
|
||||
// @alpha
|
||||
export function usePluginOptions<
|
||||
TPluginOptions extends {} = {},
|
||||
>(): TPluginOptions;
|
||||
|
||||
// @public
|
||||
export function useRouteRef<Optional extends boolean, Params extends AnyParams>(
|
||||
routeRef: ExternalRouteRef<Params, Optional>,
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
*/
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { AnalyticsContext } from '../analytics/AnalyticsContext';
|
||||
import { AnalyticsContext } from '../analytics';
|
||||
import { useApp } from '../app';
|
||||
import { RouteRef, useRouteRef } from '../routing';
|
||||
import { attachComponentData } from './componentData';
|
||||
import { Extension, BackstagePlugin } from '../plugin/types';
|
||||
import { Extension, BackstagePlugin } from '../plugin';
|
||||
import { PluginErrorBoundary } from './PluginErrorBoundary';
|
||||
import { PluginProvider } from '../plugin-options';
|
||||
|
||||
/**
|
||||
* Lazy or synchronous retrieving of extension components.
|
||||
@@ -245,7 +246,9 @@ export function createReactExtension<
|
||||
...(mountPoint && { routeRef: mountPoint.id }),
|
||||
}}
|
||||
>
|
||||
<Component {...props} />
|
||||
<PluginProvider plugin={plugin}>
|
||||
<Component {...props} />
|
||||
</PluginProvider>
|
||||
</AnalyticsContext>
|
||||
</PluginErrorBoundary>
|
||||
</Suspense>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
export * from './analytics';
|
||||
export * from './apis';
|
||||
export * from './plugin-options';
|
||||
export * from './app';
|
||||
export * from './extensions';
|
||||
export * from './icons';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 { usePluginOptions, PluginProvider } from './usePluginOptions';
|
||||
export type { PluginOptionsProviderProps } from './usePluginOptions';
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 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 React from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { usePluginOptions, PluginProvider } from './usePluginOptions';
|
||||
import { createPlugin } from '../plugin';
|
||||
|
||||
describe('usePluginOptions', () => {
|
||||
it('should provide a versioned value to hook', () => {
|
||||
type TestInputPluginOptions = {
|
||||
'key-1': string;
|
||||
};
|
||||
|
||||
type TestPluginOptions = {
|
||||
'key-1': string;
|
||||
'key-2': string;
|
||||
};
|
||||
|
||||
const plugin = createPlugin({
|
||||
id: 'my-plugin',
|
||||
__experimentalConfigure(_: TestInputPluginOptions): TestPluginOptions {
|
||||
return { 'key-1': 'value-1', 'key-2': 'value-2' };
|
||||
},
|
||||
});
|
||||
|
||||
const rendered = renderHook(() => usePluginOptions(), {
|
||||
wrapper: ({ children }) => (
|
||||
<PluginProvider plugin={plugin}>{children}</PluginProvider>
|
||||
),
|
||||
});
|
||||
|
||||
const config = rendered.result.current;
|
||||
|
||||
expect(config).toEqual({
|
||||
'key-1': 'value-1',
|
||||
'key-2': 'value-2',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
useVersionedContext,
|
||||
} from '@backstage/version-bridge';
|
||||
import { BackstagePlugin } from '../plugin';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
const contextKey: string = 'plugin-context';
|
||||
|
||||
/**
|
||||
* Properties for the PluginProvider component.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface PluginOptionsProviderProps {
|
||||
children: ReactNode;
|
||||
plugin?: BackstagePlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the plugin configuration.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const PluginProvider = ({
|
||||
children,
|
||||
plugin,
|
||||
}: PluginOptionsProviderProps): JSX.Element => {
|
||||
const { Provider } = createVersionedContext<{
|
||||
1: { plugin: BackstagePlugin | undefined };
|
||||
}>(contextKey);
|
||||
|
||||
return (
|
||||
<Provider
|
||||
value={createVersionedValueMap({
|
||||
1: {
|
||||
plugin,
|
||||
},
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Grab the current entity from the context, throws if the entity has not yet been loaded
|
||||
* or is not available.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export function usePluginOptions<
|
||||
TPluginOptions extends {} = {},
|
||||
>(): TPluginOptions {
|
||||
const versionedHolder = useVersionedContext<{ 1: TPluginOptions }>(
|
||||
contextKey,
|
||||
);
|
||||
|
||||
if (!versionedHolder) {
|
||||
throw new Error('Plugin Options context is not available');
|
||||
}
|
||||
|
||||
const value = versionedHolder.atVersion(1);
|
||||
if (!value) {
|
||||
throw new Error('Plugin Options v1 is not available');
|
||||
}
|
||||
|
||||
return (
|
||||
value as unknown as {
|
||||
plugin: {
|
||||
getPluginOptions(): {};
|
||||
};
|
||||
}
|
||||
).plugin.getPluginOptions() as TPluginOptions;
|
||||
}
|
||||
@@ -30,9 +30,18 @@ import { AnyApiFactory } from '../apis';
|
||||
export class PluginImpl<
|
||||
Routes extends AnyRoutes,
|
||||
ExternalRoutes extends AnyExternalRoutes,
|
||||
> implements BackstagePlugin<Routes, ExternalRoutes>
|
||||
PluginInputOptions extends {},
|
||||
> implements BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions>
|
||||
{
|
||||
constructor(private readonly config: PluginConfig<Routes, ExternalRoutes>) {}
|
||||
constructor(
|
||||
private readonly config: PluginConfig<
|
||||
Routes,
|
||||
ExternalRoutes,
|
||||
PluginInputOptions
|
||||
>,
|
||||
) {}
|
||||
|
||||
private options: {} | undefined = undefined;
|
||||
|
||||
getId(): string {
|
||||
return this.config.id;
|
||||
@@ -58,6 +67,19 @@ export class PluginImpl<
|
||||
return extension.expose(this);
|
||||
}
|
||||
|
||||
__experimentalReconfigure(options: PluginInputOptions): void {
|
||||
if (this.config.__experimentalConfigure) {
|
||||
this.options = this.config.__experimentalConfigure(options);
|
||||
}
|
||||
}
|
||||
|
||||
getPluginOptions(): {} {
|
||||
if (this.config.__experimentalConfigure && !this.options) {
|
||||
this.options = this.config.__experimentalConfigure();
|
||||
}
|
||||
return this.options ?? {};
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `plugin{${this.config.id}}`;
|
||||
}
|
||||
@@ -72,8 +94,9 @@ export class PluginImpl<
|
||||
export function createPlugin<
|
||||
Routes extends AnyRoutes = {},
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
PluginInputOptions extends {} = {},
|
||||
>(
|
||||
config: PluginConfig<Routes, ExternalRoutes>,
|
||||
): BackstagePlugin<Routes, ExternalRoutes> {
|
||||
config: PluginConfig<Routes, ExternalRoutes, PluginInputOptions>,
|
||||
): BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions> {
|
||||
return new PluginImpl(config);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
export { createPlugin } from './Plugin';
|
||||
|
||||
export type {
|
||||
AnyExternalRoutes,
|
||||
AnyRoutes,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing';
|
||||
import { AnyApiFactory } from '../apis/system';
|
||||
import { AnyApiFactory } from '../apis';
|
||||
|
||||
/**
|
||||
* Plugin extension type.
|
||||
@@ -52,6 +52,7 @@ export type AnyExternalRoutes = { [name: string]: ExternalRouteRef };
|
||||
export type BackstagePlugin<
|
||||
Routes extends AnyRoutes = {},
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
PluginInputOptions extends {} = {},
|
||||
> = {
|
||||
getId(): string;
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
@@ -62,6 +63,7 @@ export type BackstagePlugin<
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
__experimentalReconfigure(options: PluginInputOptions): void;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -82,12 +84,14 @@ export type PluginFeatureFlagConfig = {
|
||||
export type PluginConfig<
|
||||
Routes extends AnyRoutes,
|
||||
ExternalRoutes extends AnyExternalRoutes,
|
||||
PluginInputOptions extends {},
|
||||
> = {
|
||||
id: string;
|
||||
apis?: Iterable<AnyApiFactory>;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
__experimentalConfigure?(options?: PluginInputOptions): {};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"@backstage/config-loader": "^1.1.3",
|
||||
"@backstage/test-utils": "^1.1.2",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"msw": "^0.44.0"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -25,6 +25,7 @@ export const adrPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export const airbrakePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export const allurePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Config } from '@backstage/config';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const analyticsModuleGA: BackstagePlugin<{}, {}>;
|
||||
export const analyticsModuleGA: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// @public
|
||||
export class GoogleAnalytics implements AnalyticsApi {
|
||||
|
||||
@@ -10,8 +10,8 @@ import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// @public
|
||||
export const ApacheAirflowDagTable: ({
|
||||
dagIds,
|
||||
}: {
|
||||
dagIds,
|
||||
}: {
|
||||
dagIds?: string[] | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
@@ -27,6 +27,7 @@ export const apacheAirflowPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
```
|
||||
|
||||
@@ -47,7 +47,8 @@ const apiDocsPlugin: BackstagePlugin<
|
||||
},
|
||||
{
|
||||
registerApi: ExternalRouteRef<undefined, true>;
|
||||
}
|
||||
},
|
||||
{}
|
||||
>;
|
||||
export { apiDocsPlugin };
|
||||
export { apiDocsPlugin as plugin };
|
||||
|
||||
@@ -41,6 +41,7 @@ export const apolloExplorerPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
// Warning: (ae-missing-release-tag) "azureDevOpsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const azureDevOpsPlugin: BackstagePlugin<{}, {}>;
|
||||
export const azureDevOpsPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AzurePullRequestsIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -10,7 +10,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
// Warning: (ae-missing-release-tag) "badgesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const badgesPlugin: BackstagePlugin<{}, {}>;
|
||||
export const badgesPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityBadgesDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -16,6 +16,7 @@ export const bazaarPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Entity } from '@backstage/catalog-model';
|
||||
// Warning: (ae-missing-release-tag) "bitrisePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const bitrisePlugin: BackstagePlugin<{}, {}>;
|
||||
export const bitrisePlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityBitriseContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,57 @@
|
||||
# Backstage Catalog Frontend
|
||||
|
||||
This is the React frontend to customize Backstage [software
|
||||
catalog](http://backstage.io/docs/features/software-catalog/software-catalog-overview).
|
||||
This package supplies the example how it can be achieved.
|
||||
|
||||
## Installation
|
||||
|
||||
This `@internal/plugin-catalog-customized` package comes installed by default in example application of
|
||||
Backstage application.
|
||||
|
||||
To check if you already have the package, look under
|
||||
`packages/app/package.json`, in the `dependencies` block, for
|
||||
`@internal/plugin-catalog-customized`. The instructions below walk through restoring the
|
||||
plugin, if you previously removed it.
|
||||
|
||||
### Install the package
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn add --cwd packages/app @internal/plugin-catalog-customized
|
||||
```
|
||||
|
||||
### Add the plugin to your `packages/app`
|
||||
|
||||
Add the import to a file where is your plugin catalog is defined:
|
||||
|
||||
```diff
|
||||
// packages/app/src/App.tsx
|
||||
|
||||
import from '@internal/plugin-catalog-customized';
|
||||
|
||||
...
|
||||
|
||||
import {
|
||||
CatalogIndexPage,
|
||||
CatalogEntityPage,
|
||||
} from '@backstage/plugin-catalog';
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
This frontend plugin can be started in a standalone mode from directly in this
|
||||
package with `yarn start`. However, it will have limited functionality and that
|
||||
process is most convenient when developing the catalog frontend plugin itself.
|
||||
|
||||
To evaluate the catalog and have a greater amount of functionality available,
|
||||
run the entire Backstage example application from the root folder:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
This will launch both frontend and backend in the same window, populated with
|
||||
some example entities.
|
||||
@@ -0,0 +1,9 @@
|
||||
## API Report File for "@internal/plugin-catalog-customized"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
export * from '@backstage/plugin-catalog';
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@internal/plugin-catalog-customized",
|
||||
"description": "The internal Backstage Customizable plugin for browsing the Backstage catalog",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/catalog-customized"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"start": "backstage-cli package start",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"clean": "backstage-cli package clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/plugin-catalog": "^1.4.0",
|
||||
"@backstage/plugin-catalog-react": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 './plugin';
|
||||
|
||||
export * from '@backstage/plugin-catalog';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 { catalogPlugin } from '@backstage/plugin-catalog';
|
||||
|
||||
// id: 'catalog-customized'
|
||||
catalogPlugin.__experimentalReconfigure({
|
||||
createButtonTitle: 'New',
|
||||
});
|
||||
@@ -48,7 +48,8 @@ export const catalogGraphPlugin: BackstagePlugin<
|
||||
},
|
||||
true
|
||||
>;
|
||||
}
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public
|
||||
|
||||
@@ -133,6 +133,7 @@ const catalogImportPlugin: BackstagePlugin<
|
||||
{
|
||||
importPage: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { catalogImportPlugin };
|
||||
|
||||
@@ -79,6 +79,8 @@ export interface CatalogKindHeaderProps {
|
||||
initialFilter?: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "CatalogInputPluginOptions" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const catalogPlugin: BackstagePlugin<
|
||||
{
|
||||
@@ -99,7 +101,8 @@ export const catalogPlugin: BackstagePlugin<
|
||||
},
|
||||
true
|
||||
>;
|
||||
}
|
||||
},
|
||||
CatalogInputPluginOptions
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -23,8 +23,10 @@ import {
|
||||
} from '@backstage/catalog-model';
|
||||
import { TableColumn, TableProps } from '@backstage/core-components';
|
||||
import {
|
||||
createPlugin,
|
||||
IdentityApi,
|
||||
identityApiRef,
|
||||
PluginProvider,
|
||||
ProfileInfo,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
@@ -133,6 +135,22 @@ describe('DefaultCatalogPage', () => {
|
||||
};
|
||||
const storageApi = MockStorageApi.create();
|
||||
|
||||
type TestInputPluginOptions = {
|
||||
'key-1': string;
|
||||
};
|
||||
|
||||
type TestPluginOptions = {
|
||||
'key-1': string;
|
||||
'key-2': string;
|
||||
};
|
||||
|
||||
const plugin = createPlugin({
|
||||
id: 'my-plugin',
|
||||
__experimentalConfigure(_: TestInputPluginOptions): TestPluginOptions {
|
||||
return { 'key-1': 'value-1', 'key-2': 'value-2' };
|
||||
},
|
||||
});
|
||||
|
||||
const renderWrapped = (children: React.ReactNode) =>
|
||||
renderWithEffects(
|
||||
wrapInTestApp(
|
||||
@@ -144,7 +162,7 @@ describe('DefaultCatalogPage', () => {
|
||||
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
<PluginProvider plugin={plugin}>{children}</PluginProvider>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
|
||||
@@ -39,6 +39,7 @@ import React from 'react';
|
||||
import { createComponentRouteRef } from '../../routes';
|
||||
import { CatalogTable, CatalogTableRow } from '../CatalogTable';
|
||||
import { CatalogKindHeader } from '../CatalogKindHeader';
|
||||
import { useCatalogPluginOptions } from '../../options';
|
||||
|
||||
/**
|
||||
* Props for root catalog pages.
|
||||
@@ -65,6 +66,8 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) {
|
||||
useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage';
|
||||
const createComponentLink = useRouteRef(createComponentRouteRef);
|
||||
|
||||
const { createButtonTitle } = useCatalogPluginOptions();
|
||||
|
||||
return (
|
||||
<PageWithHeader title={`${orgName} Catalog`} themeId="home">
|
||||
<EntityListProvider>
|
||||
@@ -73,7 +76,7 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) {
|
||||
titleComponent={<CatalogKindHeader initialFilter={initialKind} />}
|
||||
>
|
||||
<CreateButton
|
||||
title="Create Component"
|
||||
title={createButtonTitle}
|
||||
to={createComponentLink && createComponentLink()}
|
||||
/>
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 { usePluginOptions } from '@backstage/core-plugin-api';
|
||||
|
||||
export type CatalogPluginOptions = {
|
||||
createButtonTitle: string;
|
||||
};
|
||||
|
||||
export type CatalogInputPluginOptions = {
|
||||
createButtonTitle: string;
|
||||
};
|
||||
|
||||
export const useCatalogPluginOptions = () =>
|
||||
usePluginOptions<CatalogPluginOptions>();
|
||||
@@ -43,6 +43,7 @@ import { HasSubcomponentsCardProps } from './components/HasSubcomponentsCard';
|
||||
import { HasSystemsCardProps } from './components/HasSystemsCard';
|
||||
import { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard';
|
||||
import { rootRouteRef } from './routes';
|
||||
import { CatalogInputPluginOptions, CatalogPluginOptions } from './options';
|
||||
|
||||
/** @public */
|
||||
export const catalogPlugin = createPlugin({
|
||||
@@ -72,6 +73,14 @@ export const catalogPlugin = createPlugin({
|
||||
createComponent: createComponentRouteRef,
|
||||
viewTechDoc: viewTechDocRouteRef,
|
||||
},
|
||||
__experimentalConfigure(
|
||||
options?: CatalogInputPluginOptions,
|
||||
): CatalogPluginOptions {
|
||||
const defaultOptions = {
|
||||
createButtonTitle: 'Create',
|
||||
};
|
||||
return { ...defaultOptions, ...options };
|
||||
},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -107,6 +107,7 @@ export const cicdStatisticsPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0",
|
||||
"@types/luxon": "^2.0.5"
|
||||
"@types/luxon": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^1.1.0",
|
||||
|
||||
@@ -75,7 +75,7 @@ export const circleCIBuildRouteRef: SubRouteRef<PathParams<'/:buildId'>>;
|
||||
// Warning: (ae-missing-release-tag) "circleCIPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
const circleCIPlugin: BackstagePlugin<{}, {}>;
|
||||
const circleCIPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
export { circleCIPlugin };
|
||||
export { circleCIPlugin as plugin };
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ const cloudbuildPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { cloudbuildPlugin };
|
||||
|
||||
@@ -48,6 +48,7 @@ export const codeClimatePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/humanize-duration": "^3.27.1",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/luxon": "^2.0.9",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/node": "^16.11.26",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.44.0"
|
||||
|
||||
@@ -14,6 +14,7 @@ export const codeCoveragePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export const codescenePlugin: BackstagePlugin<
|
||||
projectId: string;
|
||||
}>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export const configSchemaPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -382,6 +382,7 @@ const costInsightsPlugin: BackstagePlugin<
|
||||
growthAlerts: RouteRef<undefined>;
|
||||
unlabeledDataflowAlerts: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { costInsightsPlugin };
|
||||
|
||||
@@ -9,7 +9,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
// @public
|
||||
export const dynatracePlugin: BackstagePlugin<{}, {}>;
|
||||
export const dynatracePlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// @public
|
||||
export const DynatraceTab: () => JSX.Element;
|
||||
|
||||
@@ -16,6 +16,7 @@ export const todoListPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ const explorePlugin: BackstagePlugin<
|
||||
},
|
||||
true
|
||||
>;
|
||||
}
|
||||
},
|
||||
{}
|
||||
>;
|
||||
export { explorePlugin };
|
||||
export { explorePlugin as plugin };
|
||||
|
||||
@@ -20,6 +20,7 @@ export const firehydrantPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
```
|
||||
|
||||
@@ -40,6 +40,7 @@ export const fossaPlugin: BackstagePlugin<
|
||||
{
|
||||
fossaOverview: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
```
|
||||
|
||||
@@ -65,6 +65,7 @@ export const gcalendarPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ const gcpProjectsPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { gcpProjectsPlugin };
|
||||
|
||||
@@ -203,6 +203,7 @@ export const gitReleaseManagerPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -198,6 +198,7 @@ const githubActionsPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { githubActionsPlugin };
|
||||
|
||||
@@ -47,7 +47,7 @@ export const EntityGithubDeploymentsCard: (props: {
|
||||
// Warning: (ae-missing-release-tag) "githubDeploymentsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const githubDeploymentsPlugin: BackstagePlugin<{}, {}>;
|
||||
export const githubDeploymentsPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "GithubDeploymentsTableProps" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "GithubDeploymentsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
||||
@@ -149,6 +149,7 @@ const gitopsProfilesPlugin: BackstagePlugin<
|
||||
}>;
|
||||
createPage: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { gitopsProfilesPlugin };
|
||||
|
||||
@@ -15,7 +15,7 @@ export const EntityGoCdContent: () => JSX.Element;
|
||||
export const GOCD_PIPELINES_ANNOTATION = 'gocd.org/pipelines';
|
||||
|
||||
// @public
|
||||
export const gocdPlugin: BackstagePlugin<{}, {}>;
|
||||
export const gocdPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// @public
|
||||
export const isGoCdAvailable: (entity: Entity) => boolean;
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.173",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/node": "^16.11.26",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.44.0"
|
||||
|
||||
@@ -39,7 +39,7 @@ export const GraphiQLIcon: IconComponent;
|
||||
export const GraphiQLPage: () => JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
const graphiqlPlugin: BackstagePlugin<{}, {}>;
|
||||
const graphiqlPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
export { graphiqlPlugin };
|
||||
export { graphiqlPlugin as plugin };
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ export const homePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -249,6 +249,7 @@ const ilertPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { ilertPlugin };
|
||||
|
||||
@@ -108,6 +108,7 @@ const jenkinsPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { jenkinsPlugin };
|
||||
|
||||
@@ -34,6 +34,7 @@ const kafkaPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { kafkaPlugin };
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"@google-cloud/container": "^4.0.0",
|
||||
"@kubernetes/client-node": "^0.17.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"aws-sdk": "^2.840.0",
|
||||
"aws4": "^1.11.0",
|
||||
"compression": "^1.7.4",
|
||||
|
||||
@@ -353,6 +353,7 @@ const kubernetesPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { kubernetesPlugin };
|
||||
|
||||
@@ -175,6 +175,7 @@ const lighthousePlugin: BackstagePlugin<
|
||||
root: RouteRef<undefined>;
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { lighthousePlugin };
|
||||
|
||||
@@ -42,6 +42,7 @@ export const newRelicDashboardPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const newRelicPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { newRelicPlugin };
|
||||
|
||||
@@ -58,7 +58,8 @@ const orgPlugin: BackstagePlugin<
|
||||
{},
|
||||
{
|
||||
catalogIndex: ExternalRouteRef<undefined, false>;
|
||||
}
|
||||
},
|
||||
{}
|
||||
>;
|
||||
export { orgPlugin };
|
||||
export { orgPlugin as plugin };
|
||||
|
||||
@@ -166,7 +166,7 @@ export type PagerDutyOnCallsResponse = {
|
||||
// Warning: (ae-missing-release-tag) "pagerDutyPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
const pagerDutyPlugin: BackstagePlugin<{}, {}>;
|
||||
const pagerDutyPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
export { pagerDutyPlugin };
|
||||
export { pagerDutyPlugin as plugin };
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ export class PeriskopClient implements PeriskopApi {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const periskopPlugin: BackstagePlugin<{}, {}>;
|
||||
export const periskopPlugin: BackstagePlugin<{}, {}, {}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface RequestHeaders {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/luxon": "^2.0.4",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/node": "^16.11.26",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.44.0"
|
||||
|
||||
@@ -91,6 +91,7 @@ const rollbarPlugin: BackstagePlugin<
|
||||
{
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
export { rollbarPlugin as plugin };
|
||||
|
||||
@@ -321,6 +321,19 @@ export function createPublishGerritAction(options: {
|
||||
sourcePath?: string | undefined;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export function createPublishGerritReviewAction(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
config: Config;
|
||||
}): TemplateAction<{
|
||||
repoUrl: string;
|
||||
branch?: string | undefined;
|
||||
sourcePath?: string | undefined;
|
||||
gitCommitMessage?: string | undefined;
|
||||
gitAuthorName?: string | undefined;
|
||||
gitAuthorEmail?: string | undefined;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export function createPublishGithubAction(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
@@ -406,6 +419,7 @@ export const createPublishGitlabMergeRequestAction: (options: {
|
||||
branchName: string;
|
||||
targetPath: string;
|
||||
token?: string | undefined;
|
||||
commitAction?: 'update' | 'create' | 'delete' | undefined;
|
||||
projectid?: string | undefined;
|
||||
removeSourceBranch?: boolean | undefined;
|
||||
assignee?: string | undefined;
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
createPublishBitbucketCloudAction,
|
||||
createPublishBitbucketServerAction,
|
||||
createPublishGerritAction,
|
||||
createPublishGerritReviewAction,
|
||||
createPublishGithubAction,
|
||||
createPublishGithubPullRequestAction,
|
||||
createPublishGitlabAction,
|
||||
@@ -118,6 +119,10 @@ export const createBuiltinActions = (
|
||||
integrations,
|
||||
config,
|
||||
}),
|
||||
createPublishGerritReviewAction({
|
||||
integrations,
|
||||
config,
|
||||
}),
|
||||
createPublishGithubAction({
|
||||
integrations,
|
||||
config,
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
*/
|
||||
|
||||
import { Git, getVoidLogger } from '@backstage/backend-common';
|
||||
import { initRepoAndPush } from './helpers';
|
||||
import { commitAndPushRepo, initRepoAndPush } from './helpers';
|
||||
|
||||
jest.mock('@backstage/backend-common', () => ({
|
||||
Git: {
|
||||
fromAuth: jest.fn().mockReturnValue({
|
||||
init: jest.fn(),
|
||||
add: jest.fn(),
|
||||
checkout: jest.fn(),
|
||||
commit: jest.fn(),
|
||||
fetch: jest.fn(),
|
||||
addRemote: jest.fn(),
|
||||
push: jest.fn(),
|
||||
}),
|
||||
@@ -146,3 +148,142 @@ describe('initRepoAndPush', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('commitAndPushRepo', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('with minimal parameters', () => {
|
||||
beforeEach(async () => {
|
||||
await commitAndPushRepo({
|
||||
dir: '/test/repo/dir/',
|
||||
auth: {
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
},
|
||||
logger: getVoidLogger(),
|
||||
commitMessage: 'commit message',
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches commits', () => {
|
||||
expect(mockedGit.fetch).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
});
|
||||
});
|
||||
|
||||
it('checkouts to master', () => {
|
||||
expect(mockedGit.checkout).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
ref: 'master',
|
||||
});
|
||||
});
|
||||
|
||||
it('stages all files in the repo', () => {
|
||||
expect(mockedGit.add).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
filepath: '.',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a commit', () => {
|
||||
expect(mockedGit.commit).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
message: 'commit message',
|
||||
author: {
|
||||
name: 'Scaffolder',
|
||||
email: 'scaffolder@backstage.io',
|
||||
},
|
||||
committer: {
|
||||
name: 'Scaffolder',
|
||||
email: 'scaffolder@backstage.io',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('pushes to the remote', () => {
|
||||
expect(mockedGit.push).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
remote: 'origin',
|
||||
remoteRef: 'refs/heads/master',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('allows overriding the default branch', async () => {
|
||||
await commitAndPushRepo({
|
||||
dir: '/test/repo/dir/',
|
||||
auth: {
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
},
|
||||
logger: getVoidLogger(),
|
||||
commitMessage: 'commit message',
|
||||
branch: 'otherbranch',
|
||||
});
|
||||
|
||||
expect(mockedGit.checkout).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
ref: 'otherbranch',
|
||||
});
|
||||
expect(mockedGit.push).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
remote: 'origin',
|
||||
remoteRef: 'refs/heads/otherbranch',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows overriding the remote ref', async () => {
|
||||
await commitAndPushRepo({
|
||||
dir: '/test/repo/dir/',
|
||||
auth: {
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
},
|
||||
logger: getVoidLogger(),
|
||||
commitMessage: 'commit message',
|
||||
remoteRef: 'refs/for/master',
|
||||
});
|
||||
|
||||
expect(mockedGit.checkout).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
ref: 'master',
|
||||
});
|
||||
expect(mockedGit.push).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
remote: 'origin',
|
||||
remoteRef: 'refs/for/master',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows overriding the author', async () => {
|
||||
await commitAndPushRepo({
|
||||
dir: '/test/repo/dir/',
|
||||
commitMessage: 'commit message',
|
||||
gitAuthorInfo: {
|
||||
name: 'Custom Scaffolder Author',
|
||||
email: 'scaffolder@example.org',
|
||||
},
|
||||
auth: {
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
},
|
||||
logger: getVoidLogger(),
|
||||
branch: 'master',
|
||||
});
|
||||
|
||||
expect(mockedGit.commit).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
message: 'commit message',
|
||||
author: {
|
||||
name: 'Custom Scaffolder Author',
|
||||
email: 'scaffolder@example.org',
|
||||
},
|
||||
committer: {
|
||||
name: 'Custom Scaffolder Author',
|
||||
email: 'scaffolder@example.org',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -127,6 +127,53 @@ export async function initRepoAndPush({
|
||||
});
|
||||
}
|
||||
|
||||
export async function commitAndPushRepo({
|
||||
dir,
|
||||
auth,
|
||||
logger,
|
||||
commitMessage,
|
||||
gitAuthorInfo,
|
||||
branch = 'master',
|
||||
remoteRef,
|
||||
}: {
|
||||
dir: string;
|
||||
auth: { username: string; password: string };
|
||||
logger: Logger;
|
||||
commitMessage: string;
|
||||
gitAuthorInfo?: { name?: string; email?: string };
|
||||
branch?: string;
|
||||
remoteRef?: string;
|
||||
}): Promise<void> {
|
||||
const git = Git.fromAuth({
|
||||
username: auth.username,
|
||||
password: auth.password,
|
||||
logger,
|
||||
});
|
||||
|
||||
await git.fetch({ dir });
|
||||
await git.checkout({ dir, ref: branch });
|
||||
await git.add({ dir, filepath: '.' });
|
||||
|
||||
// use provided info if possible, otherwise use fallbacks
|
||||
const authorInfo = {
|
||||
name: gitAuthorInfo?.name ?? 'Scaffolder',
|
||||
email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',
|
||||
};
|
||||
|
||||
await git.commit({
|
||||
dir,
|
||||
message: commitMessage,
|
||||
author: authorInfo,
|
||||
committer: authorInfo,
|
||||
});
|
||||
|
||||
await git.push({
|
||||
dir,
|
||||
remote: 'origin',
|
||||
remoteRef: remoteRef ?? `refs/heads/${branch}`,
|
||||
});
|
||||
}
|
||||
|
||||
type BranchProtectionOptions = {
|
||||
client: Octokit;
|
||||
owner: string;
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
|
||||
jest.mock('../helpers');
|
||||
|
||||
import { createPublishGerritReviewAction } from './gerritReview';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import { commitAndPushRepo } from '../helpers';
|
||||
|
||||
describe('publish:gerrit:review', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
gerrit: [
|
||||
{
|
||||
host: 'gerrithost.org',
|
||||
username: 'gerrituser',
|
||||
password: 'usertoken',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
const action = createPublishGerritReviewAction({ integrations, config });
|
||||
const mockContext = {
|
||||
input: {
|
||||
repoUrl:
|
||||
'gerrithost.org?owner=owner&workspace=parent&project=project&repo=repo',
|
||||
gitCommitMessage: 'Review from backstage',
|
||||
},
|
||||
workspacePath: 'workspace',
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error when the repoUrl is not well formed', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
repoUrl: 'gerrithost.org?workspace=w&owner=o',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/missing repo/);
|
||||
});
|
||||
|
||||
it('should throw an error when no commit message is provided', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: { repoUrl: 'gerrithost.org?workspace=w&owner=o&repo=r' },
|
||||
}),
|
||||
).rejects.toThrow(/Missing gitCommitMessage input/);
|
||||
});
|
||||
|
||||
it('should throw if there is no integration config provided', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
repoUrl: 'missing.com?workspace=w&owner=o&repo=repo',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/No matching integration configuration/);
|
||||
});
|
||||
|
||||
it('can correctly create a review', async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(commitAndPushRepo).toHaveBeenCalledWith({
|
||||
dir: mockContext.workspacePath,
|
||||
auth: { username: 'gerrituser', password: 'usertoken' },
|
||||
logger: mockContext.logger,
|
||||
commitMessage: expect.stringContaining(
|
||||
'Review from backstage\n\nChange-Id:',
|
||||
),
|
||||
gitAuthorInfo: {},
|
||||
branch: 'master',
|
||||
remoteRef: 'refs/for/master',
|
||||
});
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'repoContentsUrl',
|
||||
'https://gerrithost.org/repo/+/refs/heads/master',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'reviewUrl',
|
||||
expect.stringMatching(new RegExp('^https://gerrithost.org/#/q/I')),
|
||||
);
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user