From b349720c7507d611aea7239d55cc220a3d20f325 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Mon, 17 Aug 2020 17:06:28 +0200 Subject: [PATCH 01/70] =migrations https logic to new branch --- microsite/i18n/en.json | 3 + microsite/pages/en/background.js | 2 +- packages/backend-common/package.json | 3 +- .../src/service/lib/ServiceBuilderImpl.ts | 120 ++++++++++++++++-- .../backend-common/src/service/lib/config.ts | 70 ++++++++++ packages/backend-common/src/service/types.ts | 19 +++ yarn.lock | 2 +- 7 files changed, 204 insertions(+), 15 deletions(-) diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index af891426db..5ddede3d97 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -50,6 +50,9 @@ "auth/add-auth-provider": { "title": "Adding authentication providers" }, + "auth/auth-backend-classes": { + "title": "auth/auth-backend-classes" + }, "auth/auth-backend": { "title": "Auth backend" }, diff --git a/microsite/pages/en/background.js b/microsite/pages/en/background.js index 70a15421ab..c5bf9bee94 100644 --- a/microsite/pages/en/background.js +++ b/microsite/pages/en/background.js @@ -112,7 +112,7 @@ const Background = props => { left: 0, right: 0, bottom: 0, - backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg);`, + backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg)`, }} /> diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 5cf2ee95e5..8d932cfd49 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -43,7 +43,8 @@ "lodash": "^4.17.15", "morgan": "^1.10.0", "stoppable": "^1.1.0", - "winston": "^3.2.1" + "winston": "^3.2.1", + "selfsigned": "^1.10.7" }, "peerDependencies": { "pg-connection-string": "^2.3.0" diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index cd8e445dd8..1182cc6388 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -19,7 +19,9 @@ import compression from 'compression'; import cors from 'cors'; import express, { Router } from 'express'; import helmet from 'helmet'; -import { Server } from 'http'; +import * as http from 'http'; +import * as https from 'https'; +import * as fs from 'fs'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; @@ -30,7 +32,12 @@ import { requestLoggingHandler, } from '../../middleware'; import { ServiceBuilder } from '../types'; -import { readBaseOptions, readCorsOptions } from './config'; +import { + readBaseOptions, + readCorsOptions, + readHttpsSettings, + HttpsSettings, +} from './config'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -41,6 +48,7 @@ export class ServiceBuilderImpl implements ServiceBuilder { private host: string | undefined; private logger: Logger | undefined; private corsOptions: cors.CorsOptions | undefined; + private httpsSettings: HttpsSettings | undefined; private routers: [string, Router][]; // Reference to the module where builder is created - needed for hot module // reloading @@ -70,6 +78,11 @@ export class ServiceBuilderImpl implements ServiceBuilder { this.corsOptions = corsOptions; } + const httpsSettings = readHttpsSettings(backendConfig); + if (httpsSettings) { + this.httpsSettings = httpsSettings; + } + return this; } @@ -88,6 +101,11 @@ export class ServiceBuilderImpl implements ServiceBuilder { return this; } + setHttpsSettings(settings: HttpsSettings): ServiceBuilder { + this.httpsSettings = settings; + return this; + } + enableCors(options: cors.CorsOptions): ServiceBuilder { this.corsOptions = options; return this; @@ -98,9 +116,15 @@ export class ServiceBuilderImpl implements ServiceBuilder { return this; } - start(): Promise { + start(): Promise { const app = express(); - const { port, host, logger, corsOptions } = this.getOptions(); + const { + port, + host, + logger, + corsOptions, + httpsSettings, + } = this.getOptions(); app.use(helmet()); if (corsOptions) { @@ -121,20 +145,90 @@ export class ServiceBuilderImpl implements ServiceBuilder { reject(e); }); - const server = stoppable( - app.listen(port, host, () => { - logger.info(`Listening on ${host}:${port}`); - }), - 0, - ); + let server: http.Server; + + if (httpsSettings) { + logger.info('Initializing https server'); + + const credentials: { key: string; cert: string } = { + key: '', + cert: '', + }; + const signingOptions: any = httpsSettings?.certificate; + + if (signingOptions?.algorithm !== undefined) { + logger.info('Generating self-signed certificate with attributes'); + + const certificateAttributes: Array = Object.entries( + signingOptions.attributes, + ).map(([name, value]) => ({ name, value })); + + // TODO: Create a type def for selfsigned. + const signatures = require('selfsigned').generate( + certificateAttributes, + { + algorithm: signingOptions?.algorithm, + keySize: signingOptions?.size || 2048, + days: signingOptions?.days || 30, + }, + ); + + logger.info( + 'Bootstrapping key and cert from self-signed certificate', + ); + + credentials.key = signatures.private; + credentials.cert = signatures.cert; + } else { + if (fs.existsSync(signingOptions?.key)) { + if (fs.lstatSync(signingOptions?.key).isFile()) { + logger.info('Bootstrapping key from file'); + + credentials.key = fs.readFileSync(signingOptions?.key).toString(); + } + } else { + logger.info('Bootstrapping key from config'); + + credentials.key = signingOptions?.key; + } + + if (fs.existsSync(signingOptions?.cert)) { + if (fs.lstatSync(signingOptions?.cert).isFile()) { + logger.info('Bootstrapping cert from file'); + + credentials.cert = fs + .readFileSync(signingOptions?.cert) + .toString(); + } + } else { + logger.info('Bootstrapping cert from config'); + + credentials.cert = signingOptions?.cert; + } + } + + if (credentials.key === '' || credentials.cert === '') { + throw new Error('Invalid credentials'); + } + + server = https.createServer(credentials, app) as http.Server; + } else { + logger.info('Initializing http server'); + + server = http.createServer(app); + } + + const stoppableServer = stoppable(server, 0); + + stoppableServer.listen(port, host); useHotCleanup(this.module, () => - server.stop((e: any) => { + stoppableServer.stop((e: any) => { if (e) console.error(e); }), ); - resolve(server); + resolve(stoppableServer); }); } @@ -143,12 +237,14 @@ export class ServiceBuilderImpl implements ServiceBuilder { host: string; logger: Logger; corsOptions?: cors.CorsOptions; + httpsSettings?: HttpsSettings; } { return { port: this.port ?? DEFAULT_PORT, host: this.host ?? DEFAULT_HOST, logger: this.logger ?? getRootLogger(), corsOptions: this.corsOptions, + httpsSettings: this.httpsSettings, }; } } diff --git a/packages/backend-common/src/service/lib/config.ts b/packages/backend-common/src/service/lib/config.ts index 49f8dc4f04..e257bd4111 100644 --- a/packages/backend-common/src/service/lib/config.ts +++ b/packages/backend-common/src/service/lib/config.ts @@ -22,6 +22,41 @@ export type BaseOptions = { listenHost?: string; }; +export type CertificateOptions = { + key?: CertificateKeyOptions; + attributes?: CertificateAttributeOptions; +}; + +export type CertificateKeyOptions = { + size?: number; + algorithm?: string; + days?: number; +}; + +export type CertificateAttributeOptions = { + commonName?: string; +}; + +export type HttpsSettings = { + certificate: CertificateSigningOptions | CertificateReferenceOptions; +}; + +export type CertificateReferenceOptions = { + key: string; + cert: string; +}; + +export type CertificateSigningOptions = { + algorithm: string; + size?: number; + days?: number; + attributes?: CertificateAttributes; +}; + +export type CertificateAttributes = { + commonName?: string; +}; + /** * Reads some base options out of a config object. * @@ -40,6 +75,7 @@ export function readBaseOptions(config: ConfigReader): BaseOptions { if (typeof config.get('listen') === 'string') { // TODO(freben): Expand this to support more addresses and perhaps optional const { host, port } = parseListenAddress(config.getString('listen')); + return removeUnknown({ listenPort: port, listenHost: host, @@ -49,6 +85,7 @@ export function readBaseOptions(config: ConfigReader): BaseOptions { return removeUnknown({ listenPort: config.getOptionalNumber('listen.port'), listenHost: config.getOptionalString('listen.host'), + baseUrl: config.getOptionalString('baseUrl'), }); } @@ -86,6 +123,39 @@ export function readCorsOptions(config: ConfigReader): CorsOptions | undefined { }); } +/** + * Attempts to read a https settings object from the root of a config object. + * + * @param config The root of a backend config object + * @returns A https settings object, or undefined if not specified + * + * @example + * ```json + * { + * https: { + * certificate: ... + * } + * } + * ``` + */ +export function readHttpsSettings( + config: ConfigReader, +): HttpsSettings | undefined { + const cc = config.getOptionalConfig('https'); + + if (!cc) { + return undefined; + } + + const certificateConfig = cc.get('certificate'); + + const cfg = { + certificate: certificateConfig, + }; + + return removeUnknown(cfg as HttpsSettings); +} + function getOptionalStringOrStrings( config: ConfigReader, key: string, diff --git a/packages/backend-common/src/service/types.ts b/packages/backend-common/src/service/types.ts index 070794969f..d389f4b5ff 100644 --- a/packages/backend-common/src/service/types.ts +++ b/packages/backend-common/src/service/types.ts @@ -19,6 +19,7 @@ import cors from 'cors'; import { Router, RequestHandler } from 'express'; import { Server } from 'http'; import { Logger } from 'winston'; +import { HttpsSettings } from './lib/config'; export type ServiceBuilder = { /** @@ -39,6 +40,15 @@ export type ServiceBuilder = { */ setPort(port: number): ServiceBuilder; + /** + * Sets the host to listen on. + * + * '' is express default, which listens to all interfaces. + * + * @param host The host to listen on + */ + setHost(host: string): ServiceBuilder; + /** * Sets the logger to use for service-specific logging. * @@ -58,6 +68,15 @@ export type ServiceBuilder = { */ enableCors(options: cors.CorsOptions): ServiceBuilder; + /** + * Configure self-signed certificate generation options. + * + * If this method is not called, the resulting service will use sensible defaults + * + * @param options Standard certificate options + */ + setHttpsSettings(settings: HttpsSettings): ServiceBuilder; + /** * Adds a router (similar to the express .use call) to the service. * diff --git a/yarn.lock b/yarn.lock index 6a63b34141..9ab7af396b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9349,7 +9349,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -docusaurus@2.0.0-alpha.61: +docusaurus@^2.0.0-alpha.61: version "2.0.0-alpha.61" resolved "https://registry.npmjs.org/docusaurus/-/docusaurus-2.0.0-alpha.61.tgz#f347b81c98c66f1de3ecfccf63fa421ddff52fbb" integrity sha512-qDU3nOA4Xs95tIjjSETnEuRmTukTzgxyTZ5MgMyuG7y6h4oDHtpLcYf8F+xlXuuWHKv3VSxRJNzV8fZHPgnK3g== From d982278fc34450f59c5f237502413ae92c3cb4bc Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 10:27:48 +0200 Subject: [PATCH 02/70] =fixed broken microsite --- microsite/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/package.json b/microsite/package.json index 6a10628b5a..dd8957245a 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -6,7 +6,7 @@ "scripts": { "examples": "docusaurus-examples", "start": "docusaurus-start", - "build": "docusaurus-build && cp static/img/*.svg build/backstage/img/", + "build": "docusaurus-build", "publish-gh-pages": "docusaurus-publish", "write-translations": "docusaurus-write-translations", "version": "docusaurus-version", From 44e09bd980afb1cf936ddbe6679eb7f34239dde9 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 10:37:38 +0200 Subject: [PATCH 03/70] =added disabled config for https w. self-signed cert --- app-config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app-config.yaml b/app-config.yaml index a0233991f8..f3b3b8d00c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -13,6 +13,13 @@ backend: database: client: sqlite3 connection: ':memory:' + # https: + # certificate: + # size: 2048 + # algorithm: sha256 + # days: 30 + # attributes: + # commonName: 'contoso.com' proxy: '/circleci/api': From 141bb0b9ab3c902b0c7715e606b251a2fd4ac7ef Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 16:53:37 +0200 Subject: [PATCH 04/70] =triggering checks to see why CLI tests timed out --- app-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-config.yaml b/app-config.yaml index f3b3b8d00c..0e94367aa1 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -19,7 +19,7 @@ backend: # algorithm: sha256 # days: 30 # attributes: - # commonName: 'contoso.com' + # commonName: 'dfds.com' proxy: '/circleci/api': From 0ee6ba404905cce6ddcfcd052dbcb7b56704333d Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 11:11:53 +0200 Subject: [PATCH 05/70] =Fixed problem with the stoppable wrapper not triggering the listen method on the http server object --- .../backend-common/src/service/lib/ServiceBuilderImpl.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 1182cc6388..806096915b 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -218,9 +218,12 @@ export class ServiceBuilderImpl implements ServiceBuilder { server = http.createServer(app); } - const stoppableServer = stoppable(server, 0); + const stoppableServer = stoppable( + server.listen(port, host, () => { + logger.info(`Listening on ${host}:${port}`); + }), 0); - stoppableServer.listen(port, host); + //stoppableServer.listen(port, host); useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { From d263219c2b8aa55a82ead09c2adf2a79cc28678f Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 11:12:16 +0200 Subject: [PATCH 06/70] =Fixed problem with the stoppable wrapper not triggering the listen method on the http server object --- packages/backend-common/src/service/lib/ServiceBuilderImpl.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 806096915b..0e0feb6a37 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -223,8 +223,6 @@ export class ServiceBuilderImpl implements ServiceBuilder { logger.info(`Listening on ${host}:${port}`); }), 0); - //stoppableServer.listen(port, host); - useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { if (e) console.error(e); From 17233abfcbc37915b0d9158c3284811ef49c243a Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 14:55:42 +0200 Subject: [PATCH 07/70] =Partial commit, have to go home --- .../src/service/lib/ServiceBuilderImpl.ts | 6 ++- .../src/service/lib/hostFactory.ts | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 packages/backend-common/src/service/lib/hostFactory.ts diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 0e0feb6a37..e75ae8069f 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -38,6 +38,10 @@ import { readHttpsSettings, HttpsSettings, } from './config'; +import { + createHttpServer, + createHttpsServer, +} from './hostFactory'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -215,7 +219,7 @@ export class ServiceBuilderImpl implements ServiceBuilder { } else { logger.info('Initializing http server'); - server = http.createServer(app); + server = createHttpServer(app); } const stoppableServer = stoppable( diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts new file mode 100644 index 0000000000..9b850431fb --- /dev/null +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import express from 'express'; +import * as http from 'http'; +import * as https from 'https'; + +/** + * Reads some base options out of a config object. + * + * @param config The root of a backend config object + * @returns A base options object + * + * @example + * ```json + * { + * baseUrl: "http://localhost:7000", + * listen: "0.0.0.0:7000" + * } + * ``` + */ +export function createHttpServer(app: express.Express): http.Server { + return http.createServer(app); +} + + +export function createHttpsServer(app: express.Express): http.Server { + return https.createServer(app); +} \ No newline at end of file From 89a0a2fc7b54ed82ccdf3dae43883963fa0d7d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 19 Aug 2020 15:52:18 +0200 Subject: [PATCH 08/70] docs: write call-existing-api.md --- docs/plugins/call-existing-api.md | 175 +++++++++++++++++++++++++++++- plugins/jenkins/README.md | 2 +- 2 files changed, 174 insertions(+), 3 deletions(-) diff --git a/docs/plugins/call-existing-api.md b/docs/plugins/call-existing-api.md index 075d4bdeb6..f6c9990b90 100644 --- a/docs/plugins/call-existing-api.md +++ b/docs/plugins/call-existing-api.md @@ -1,6 +1,177 @@ --- id: call-existing-api -title: Call existing API +title: Call Existing API --- -## TODO +This article describes the various options that Backstage frontend plugins have, +in communicating with service APIs that already exist. Each section below +describes a possible choice, and the circumstances under which it fits. + +In these examples, we will be ultimately requesting data from the fictional +FrobsCo API. + +## Issuing Requests Directly + +The most basic choice available is to issue requests directly from the plugin +frontend code to the FrobsCo API, using for example `fetch` or a support library +such as `axios`. + +Example: + +```ts +// Inside your component +fetch('https://api.frobsco.com/v1/list') + .then(response => response.json()) + .then(payload => setFrobs(payload as Frob[])); +``` + +Internally at Spotify, this has not been a very common choice. Third party APIs +are sometimes accessed like this. Just a handful of internal APIs also went +through the trouble of exposing themselves in a way that is useful directly from +a browser, but even then, often not from the public internet but only supporting +users that are already on the company VPN. + +This can be used when: + +- The API already does/exposes exactly what you need. +- The request/response patterns of the API match real world usage needs in + Backstage frontend plugins. For example, if the end use case is to show a + small summary in Backstage, but the only available API endpoint gives a 30 + megabyte blob with large amounts of redundant information, it would hurt the + end user experience. Particularly on mobile. The same goes for cases where you + want to show many individual pieces of information: if a common use case is to + show large tables where one API request per cell is necessary, the browser + will quickly become swamped and you may want to consider performing + aggregation elsewhere instead. +- The API can maintain interactive request/response times at your required peak + request rates. The end user experience will be degraded if they spend a lot of + time waiting for the data to arrive. +- The API endpoint is highly available. The browser does not have builtin + facilities for load balancing, service discovery, retries, health checks, + circuit breaking and similar. If the endpoint is occasionally down even for + short periods of time (e.g. during deploys), end users will quickly notice. +- The API is exposed over HTTPS (not just HTTP), and properly handles + [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). These are + requirements that the user's browser will impose for security reasons, and the + requests will be rejected otherwise. +- The API endpoint is easily reachable, in terms of network conditions, by end + users. This may be particularly relevant if your end users are outside of your + perimeter. +- The requests do not require secrets to be passed. This limitation does not + apply to OAuth tokens, which the frontend can negotiate and make proper use + of. + +## Using The Backstage Proxy + +Backstage has an optional proxy plugin for the backend, that can be used to +easily add proxy routes to downstream APIs. + +Example: + +```yaml +# In app-config.yaml +proxy: + '/frobs': + target: 'http://api.frobsco.com/v1' + changeOrigin: true + pathRewrite: + '^/proxy/frobs/': '/' +``` + +```ts +// Inside your component +const backendUrl = config.getString('backend.baseUrl'); +fetch(`${backendUrl}/proxy/frobs/list`) + .then(response => response.json()) + .then(payload => setFrobs(payload as Frob[])); +``` + +The proxy is powered by the `http-proxy-middleware` package, and supports all of +its +[configuration options](https://github.com/chimurai/http-proxy-middleware#options). + +Internally at Spotify, the proxy option has been the overwhelmingly most popular +choice for plugin makers. Since we have DNS based service discovery in place and +a microservices framework that made it trivial to expose plain HTTP, it has been +a matter of just adding a few lines of Backstage config to get the benefit of +being easily and robustly reachable from users' web browsers as well. + +This may be used instead of direct requests, when: + +- You need to perform HTTPS termination and/or CORS handling, because the API + itself is not supplying those. +- You need to inject a simple static secret into the requests, e.g. an + Authorization header that gets added to the request headers. +- You want to make use of other proxy facilities, such as retries, failover, + health checks, routing, request logging, rewrites, etc. +- You already have the Backstage backend itself exposed through your perimeter + and find it practical to have only one entry point to deal with, governing + ingress with just the Backstage config. + +## Creating a Backstage Backend Plugin + +Much like the Backstage frontend, the Backstage backend also has a plugin +system. The above mentioned proxy is actually one such plugin. If you were in +need of a more involved integration than just direct access to the FrobsCo API, +or if you needed to hold state, you may want to make such a plugin. + +Example: + +```ts +// Inside your component +const backendUrl = config.getString('backend.baseUrl'); +fetch(`${backendUrl}/frobs-aggregator/summary`) + .then(response => response.json()) + .then(payload => setSummary(payload as FrobSummary)); +``` + +```ts +// Inside a new frobs-aggregator backend plugin +router.use('/summary', async (req, res) => { + const agg = await Promise.all([ + fetch('https://api.frobsco.com/v1/list'), + fetch('http://flerps.partnercompany.com:8080/flerp-batch'), + database.currentThunk(), + ]).then(async ([frobs, flerps, thunk]) => { + return computeAggregate(await frobs.json(), await flerps.json(), thunk); + }); + res.status(200).send(agg); +}); +``` + +For a more detailed example, see +[the lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) +that stores some state in a database and adds new capabilities to the underlying +API. + +Internally at Spotify, this has been a fairly popular choice for different +reasons. Commonly, the backend has been used as a caching and data massaging +layer for slow APIs or APIs whose request/response shapes or speeds were not +acceptable for direct use by frontends. For example, this has made it possible +to issue efficient batch queries from the frontend, e.g. in big lists or tables +that want to resolve a lot of sparse data from the larger list that an +underlying service supplies. + +This may be used instead of the above, when: + +- You need to perform complex model conversion, or protocol translation beyond + what the proxy handles. +- You want to perform aggregations or summaries on the backend instead of on the + frontend. +- You want to enable batching or caching of slower or more unreliable APIs. +- You need to maintain state for your plugin, perhaps using the builtin database + support in the backend. +- You need to inject secrets or in other ways negotiate with other parts of the + API or other services in order to perform your work. +- You want to enforce end user authentication / authorization for operations on + behalf of the API, have session handling, or similar. + +There is a balance to strike regarding when to make an entirely separate backend +for a purpose, and when to make a Backstage backend plugin that adapts something +that already exists. General advice is not easy to give, but contact us on +Discord if you have any questions, and we may be able to offer guidance. + +## Extending the GraphQL Model + +The extensible GraphQL backend layer is not built yet. This section will be +expanded when that happens. Stay tuned! diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index b8a844f5d8..a911704dc4 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -4,7 +4,7 @@ Website: [https://jenkins.io/](https://jenkins.io/) Last master build Folder results -Build detials +Build details ## Setup From 19db25eac700058c48ee28bc60f0e0adacfa9cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 20 Aug 2020 22:16:19 +0200 Subject: [PATCH 09/70] fix(core): chunk up tabs better --- packages/core/src/components/Tabs/Tabs.tsx | 8 ++++---- packages/core/src/components/Tabs/utils.ts | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/core/src/components/Tabs/Tabs.tsx b/packages/core/src/components/Tabs/Tabs.tsx index 54369de473..336ed2b93b 100644 --- a/packages/core/src/components/Tabs/Tabs.tsx +++ b/packages/core/src/components/Tabs/Tabs.tsx @@ -48,7 +48,7 @@ export interface TabsProps { tabs: TabProps[]; } -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ root: { flexGrow: 1, width: '100%', @@ -66,7 +66,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export const Tabs: FC = ({ tabs }) => { const classes = useStyles(); - const [value, setValue] = useState([0, 0]); // [selectedChunckedNavIndex, selectedIndex] + const [value, setValue] = useState([0, 0]); // [selectedChunkedNavIndex, selectedIndex] const [navIndex, setNavIndex] = useState(0); const [numberOfChunkedElement, setNumberOfChunkedElement] = useState(0); const [chunkedTabs, setChunkedTabs] = useState([[]]); @@ -89,7 +89,7 @@ export const Tabs: FC = ({ tabs }) => { const hasNextNavIndex = () => navIndex + 1 < chunkedTabs.length; useEffect(() => { - // Each time the window is resized we calculate how many tabs wwe can render given the window width + // Each time the window is resized we calculate how many tabs we can render given the window width const padding = 20; // The AppBar padding const numberOfTabIcons = navIndex === 0 ? 1 : 2; @@ -99,7 +99,7 @@ export const Tabs: FC = ({ tabs }) => { const newChunkedElementSize = Math.floor(wrapperWidth / 170); setNumberOfChunkedElement(newChunkedElementSize); - setChunkedTabs(chunkArray([...tabs], newChunkedElementSize)); + setChunkedTabs(chunkArray(tabs, newChunkedElementSize)); setValue([ Math.floor(flattenIndex / newChunkedElementSize), flattenIndex % newChunkedElementSize, diff --git a/packages/core/src/components/Tabs/utils.ts b/packages/core/src/components/Tabs/utils.ts index 3e0ab6f2c3..8d6a3be5f3 100644 --- a/packages/core/src/components/Tabs/utils.ts +++ b/packages/core/src/components/Tabs/utils.ts @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TabProps } from './Tabs'; -export const chunkArray = ( - myArray: TabProps[], - chunkSize: number, -): TabProps[][] => { - const results = []; - while (myArray.length) { - results.push(myArray.splice(0, chunkSize)); +export function chunkArray(array: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) { + return [array]; } - return results; -}; + + const result: T[][] = []; + for (let i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + + return result; +} From 8a317bfa1c068baf068106d8aca7986b677ecf09 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 20 Aug 2020 16:59:01 +0200 Subject: [PATCH 10/70] feat: support passing --inspect to backend:dev This allows to debug the nodejs instance. --- packages/cli/src/commands/backend/dev.ts | 2 ++ packages/cli/src/index.ts | 1 + packages/cli/src/lib/bundler/backend.ts | 6 +++++- packages/cli/src/lib/bundler/config.ts | 5 ++++- packages/cli/src/lib/bundler/types.ts | 4 +++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/backend/dev.ts b/packages/cli/src/commands/backend/dev.ts index 91c08af201..a0752b94ed 100644 --- a/packages/cli/src/commands/backend/dev.ts +++ b/packages/cli/src/commands/backend/dev.ts @@ -25,9 +25,11 @@ export default async (cmd: Command) => { env: 'development', rootPaths: [paths.targetRoot, paths.targetDir], }); + const waitForExit = await serveBackend({ entry: 'src/index', checksEnabled: cmd.check, + inspectEnabled: cmd.inspect, config: ConfigReader.fromConfigs(appConfigs), appConfigs, }); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4b9dc4bab7..55ef639b2a 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -52,6 +52,7 @@ const main = (argv: string[]) => { .command('backend:dev') .description('Start local development server with HMR for the backend') .option('--check', 'Enable type checking and linting') + .option('--inspect', 'Enable debugger') .action(lazyAction(() => import('./commands/backend/dev'), 'default')); program diff --git a/packages/cli/src/lib/bundler/backend.ts b/packages/cli/src/lib/bundler/backend.ts index ce821a585b..f5f233854f 100644 --- a/packages/cli/src/lib/bundler/backend.ts +++ b/packages/cli/src/lib/bundler/backend.ts @@ -19,7 +19,11 @@ import { createBackendConfig } from './config'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; -export async function serveBackend(options: ServeOptions) { +export async function serveBackend( + options: ServeOptions & { + inspectEnabled: boolean; + }, +) { const paths = resolveBundlingPaths(options); const config = createBackendConfig(paths, { ...options, diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 8049ce436d..e0640b5133 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -200,7 +200,10 @@ export function createBackendConfig( : '[name].[chunkhash:8].chunk.js', }, plugins: [ - new StartServerPlugin('main.js'), + new StartServerPlugin({ + name: 'main.js', + nodeArgs: options.inspectEnabled ? ['--inspect'] : undefined, + }), new webpack.HotModuleReplacementPlugin(), ...(checksEnabled ? [ diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 5d0ba66a51..03d68d9bb8 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -25,7 +25,9 @@ export type BundlingOptions = { baseUrl: URL; }; -export type BackendBundlingOptions = Omit; +export type BackendBundlingOptions = Omit & { + inspectEnabled: boolean; +}; export type ServeOptions = BundlingPathsOptions & { checksEnabled: boolean; From d63f777ee0d2f460e716873d7a3f9dabd5071658 Mon Sep 17 00:00:00 2001 From: Kat Zhou <61153904+katz95@users.noreply.github.com> Date: Fri, 21 Aug 2020 09:46:02 +0200 Subject: [PATCH 11/70] new design header for design documentation --- docs/assets/dls/designheader-updated.png | Bin 0 -> 155351 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/dls/designheader-updated.png diff --git a/docs/assets/dls/designheader-updated.png b/docs/assets/dls/designheader-updated.png new file mode 100644 index 0000000000000000000000000000000000000000..56c5a56abbd65b857d61dbed59146ab428318196 GIT binary patch literal 155351 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

l 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%0DfCYtS)BFsPATpVa8`B_2Q; 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 uP#ZM`-f7k|3!GjcZ~cr4yW1Ai4dzQGd+R0~nx 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

%Qd|*XYssfv5ScpVB2`hNKtfBKx|x`vGZX( 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 p&(wb#lK Date: Fri, 21 Aug 2020 09:47:50 +0200 Subject: [PATCH 12/70] Update design.md --- docs/dls/design.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dls/design.md b/docs/dls/design.md index c8a94fad4d..23ba84024a 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -3,7 +3,7 @@ id: design title: Design --- -![header](../assets/dls/designheader.png) +![header](../assets/dls/designheader-updated.png) Much like Backstage Open Source, this is a _living_ document! We'll keep this updated as we evolve our practices! @@ -116,7 +116,7 @@ components. If you’d like to help build up our design system, you can also add components we’ve designed to the Storybook as well. **[Figma](https://www.figma.com/@backstage)** - we're stoked to be using Figma -Community to share our design assets. You can duplicate our component library +Community to share our design assets. You can duplicate our UI Kit and design your own plugin for Backstage. **[Discord](https://discord.gg/EBHEGzX)** - all design questions should be From 5bf548ac808428dcc530cc51625dd6d072d12b38 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 10:31:30 +0200 Subject: [PATCH 13/70] Show progress bar when loading on techdocs (#2065) --- plugins/techdocs/src/reader/components/Reader.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 8e1e36595a..8ae3273e12 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { useApi } from '@backstage/core'; +import { useApi, Progress } from '@backstage/core'; import { useShadowDom } from '..'; import { useAsync } from 'react-use'; import { techdocsStorageApiRef } from '../../api'; @@ -131,5 +131,10 @@ export const Reader = ({ entityId }: Props) => { return ; } - return

; + return ( + <> + {loading ? : null} +
+ + ); }; From 6e3cb6c874b66f30c22b019fef6b0cb8f1ded953 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 10:51:37 +0200 Subject: [PATCH 14/70] =removing support for loading certs via file paths, adding host factory module, removing config section --- app-config.yaml | 7 -- .../src/service/lib/ServiceBuilderImpl.ts | 87 ++----------------- .../src/service/lib/hostFactory.ts | 56 +++++++++++- 3 files changed, 60 insertions(+), 90 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 0e94367aa1..a0233991f8 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -13,13 +13,6 @@ backend: database: client: sqlite3 connection: ':memory:' - # https: - # certificate: - # size: 2048 - # algorithm: sha256 - # days: 30 - # attributes: - # commonName: 'dfds.com' proxy: '/circleci/api': diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index e75ae8069f..899db248d6 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -21,7 +21,6 @@ import express, { Router } from 'express'; import helmet from 'helmet'; import * as http from 'http'; import * as https from 'https'; -import * as fs from 'fs'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; @@ -38,10 +37,7 @@ import { readHttpsSettings, HttpsSettings, } from './config'; -import { - createHttpServer, - createHttpsServer, -} from './hostFactory'; +import { createHttpServer, createHttpsServer } from './hostFactory'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -149,83 +145,16 @@ export class ServiceBuilderImpl implements ServiceBuilder { reject(e); }); - let server: http.Server; - - if (httpsSettings) { - logger.info('Initializing https server'); - - const credentials: { key: string; cert: string } = { - key: '', - cert: '', - }; - const signingOptions: any = httpsSettings?.certificate; - - if (signingOptions?.algorithm !== undefined) { - logger.info('Generating self-signed certificate with attributes'); - - const certificateAttributes: Array = Object.entries( - signingOptions.attributes, - ).map(([name, value]) => ({ name, value })); - - // TODO: Create a type def for selfsigned. - const signatures = require('selfsigned').generate( - certificateAttributes, - { - algorithm: signingOptions?.algorithm, - keySize: signingOptions?.size || 2048, - days: signingOptions?.days || 30, - }, - ); - - logger.info( - 'Bootstrapping key and cert from self-signed certificate', - ); - - credentials.key = signatures.private; - credentials.cert = signatures.cert; - } else { - if (fs.existsSync(signingOptions?.key)) { - if (fs.lstatSync(signingOptions?.key).isFile()) { - logger.info('Bootstrapping key from file'); - - credentials.key = fs.readFileSync(signingOptions?.key).toString(); - } - } else { - logger.info('Bootstrapping key from config'); - - credentials.key = signingOptions?.key; - } - - if (fs.existsSync(signingOptions?.cert)) { - if (fs.lstatSync(signingOptions?.cert).isFile()) { - logger.info('Bootstrapping cert from file'); - - credentials.cert = fs - .readFileSync(signingOptions?.cert) - .toString(); - } - } else { - logger.info('Bootstrapping cert from config'); - - credentials.cert = signingOptions?.cert; - } - } - - if (credentials.key === '' || credentials.cert === '') { - throw new Error('Invalid credentials'); - } - - server = https.createServer(credentials, app) as http.Server; - } else { - logger.info('Initializing http server'); - - server = createHttpServer(app); - } + const server: http.Server = httpsSettings + ? createHttpsServer(app, httpsSettings, logger) + : createHttpServer(app, logger); const stoppableServer = stoppable( server.listen(port, host, () => { - logger.info(`Listening on ${host}:${port}`); - }), 0); + logger.info(`Listening on ${host}:${port}`); + }), + 0, + ); useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 9b850431fb..778601e3c4 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -16,6 +16,8 @@ import express from 'express'; import * as http from 'http'; import * as https from 'https'; +import { Logger } from 'winston'; +import { HttpsSettings } from './config'; /** * Reads some base options out of a config object. @@ -31,11 +33,57 @@ import * as https from 'https'; * } * ``` */ -export function createHttpServer(app: express.Express): http.Server { +export function createHttpServer( + app: express.Express, + logger: Logger, +): http.Server { + logger.info('Initializing http server'); + return http.createServer(app); } +export function createHttpsServer( + app: express.Express, + httpsSettings: HttpsSettings, + logger: Logger, +): http.Server { + logger.info('Initializing https server'); -export function createHttpsServer(app: express.Express): http.Server { - return https.createServer(app); -} \ No newline at end of file + const credentials: { key: string; cert: string } = { + key: '', + cert: '', + }; + + const signingOptions: any = httpsSettings?.certificate; + + if (signingOptions?.algorithm !== undefined) { + logger.info('Generating self-signed certificate with attributes'); + + const certificateAttributes: Array = Object.entries( + signingOptions.attributes, + ).map(([name, value]) => ({ name, value })); + + // TODO: Create a type def for selfsigned. + const signatures = require('selfsigned').generate(certificateAttributes, { + algorithm: signingOptions?.algorithm, + keySize: signingOptions?.size || 2048, + days: signingOptions?.days || 30, + }); + + logger.info('Bootstrapping self-signed certificate'); + + credentials.key = signatures.private; + credentials.cert = signatures.cert; + } else { + logger.info('Bootstrapping cert from config'); + + credentials.key = signingOptions?.key; + credentials.cert = signingOptions?.cert; + } + + if (credentials.key === '' || credentials.cert === '') { + throw new Error('Invalid credentials'); + } + + return https.createServer(credentials, app) as http.Server; +} From f66eb57c10bf63bee4eeebd70f89565142c8ece0 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 10:58:17 +0200 Subject: [PATCH 15/70] =add inline documentation --- .../src/service/lib/hostFactory.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 778601e3c4..8fab2fd4ab 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -20,34 +20,37 @@ import { Logger } from 'winston'; import { HttpsSettings } from './config'; /** - * Reads some base options out of a config object. + * Creates a Http server instance based on an Express application. * - * @param config The root of a backend config object - * @returns A base options object + * @param app The Express application object + * @param logger Optional Winston logger object + * @returns A Http server instance * - * @example - * ```json - * { - * baseUrl: "http://localhost:7000", - * listen: "0.0.0.0:7000" - * } - * ``` */ export function createHttpServer( app: express.Express, - logger: Logger, + logger?: Logger, ): http.Server { - logger.info('Initializing http server'); + logger?.info('Initializing http server'); return http.createServer(app); } +/** + * Creates a Https server instance based on an Express application. + * + * @param app The Express application object + * @param httpsSettings HttpsSettings for self-signed certificate generation + * @param logger Optional Winston logger object + * @returns A Https server instance + * + */ export function createHttpsServer( app: express.Express, httpsSettings: HttpsSettings, - logger: Logger, + logger?: Logger, ): http.Server { - logger.info('Initializing https server'); + logger?.info('Initializing https server'); const credentials: { key: string; cert: string } = { key: '', @@ -57,7 +60,7 @@ export function createHttpsServer( const signingOptions: any = httpsSettings?.certificate; if (signingOptions?.algorithm !== undefined) { - logger.info('Generating self-signed certificate with attributes'); + logger?.info('Generating self-signed certificate with attributes'); const certificateAttributes: Array = Object.entries( signingOptions.attributes, @@ -70,12 +73,12 @@ export function createHttpsServer( days: signingOptions?.days || 30, }); - logger.info('Bootstrapping self-signed certificate'); + logger?.info('Bootstrapping self-signed certificate'); credentials.key = signatures.private; credentials.cert = signatures.cert; } else { - logger.info('Bootstrapping cert from config'); + logger?.info('Bootstrapping cert from config'); credentials.key = signingOptions?.key; credentials.cert = signingOptions?.cert; From d8947239ad187725d002876c3375d3c48cc13607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 21 Aug 2020 11:01:46 +0200 Subject: [PATCH 16/70] fix(core): bump react-syntax-highlighter to unbreak build --- packages/core/package.json | 2 +- yarn.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 7333e0ce2a..3e33fec7a1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -50,7 +50,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", - "react-syntax-highlighter": "^13.2.1", + "react-syntax-highlighter": "^13.5.1", "react-use": "^15.3.3" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 1a56d05251..c3527d6df6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18209,14 +18209,14 @@ pretty-hrtime@^1.0.3: resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= -prismjs@^1.17.1: +prismjs@^1.17.1, prismjs@^1.21.0, prismjs@~1.21.0: version "1.21.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3" integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw== optionalDependencies: clipboard "^2.0.0" -prismjs@^1.20.0, prismjs@^1.8.4, prismjs@~1.20.0: +prismjs@^1.8.4: version "1.20.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.20.0.tgz#9b685fc480a3514ee7198eac6a3bf5024319ff03" integrity sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ== @@ -19070,16 +19070,16 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.2.1: - version "13.2.1" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.2.1.tgz#3d5a3b655cd85ce06b9508e0365d01ef9b8560bb" - integrity sha512-O/AF/ll4I3Dp+2WuKbnF5yKG4b1BUncqcpTW6MIBDJPr2eGKqlNGS3Nv+lyCFtAIHx8N+/ktti8qH14Q5VjjcA== +react-syntax-highlighter@^13.5.1: + version "13.5.1" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" + integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== dependencies: "@babel/runtime" "^7.3.1" highlight.js "^10.1.1" lowlight "^1.14.0" - prismjs "^1.20.0" - refractor "^3.0.0" + prismjs "^1.21.0" + refractor "^3.1.0" react-test-renderer@^16.13.1: version "16.13.1" @@ -19411,14 +19411,14 @@ refractor@^2.4.1: parse-entities "^1.1.2" prismjs "~1.17.0" -refractor@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/refractor/-/refractor-3.0.0.tgz#7c8072eaf49dbc1b333e7acc64fb52a1c9b17c75" - integrity sha512-eCGK/oP4VuyW/ERqjMZRZHxl2QsztbkedkYy/SxqE/+Gh1gLaAF17tWIOcVJDiyGhar1NZy/0B9dFef7J0+FDw== +refractor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/refractor/-/refractor-3.1.0.tgz#b05a43c8a1b4fccb30001ffcbd5cd781f7f06f78" + integrity sha512-bN8GvY6hpeXfC4SzWmYNQGLLF2ZakRDNBkgCL0vvl5hnpMrnyURk8Mv61v6pzn4/RBHzSWLp44SzMmVHqMGNww== dependencies: hastscript "^5.0.0" parse-entities "^2.0.0" - prismjs "~1.20.0" + prismjs "~1.21.0" regenerate-unicode-properties@^8.2.0: version "8.2.0" From 5bffcb108d36939fe43e54f9330ab560702b4e5f Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 11:03:07 +0200 Subject: [PATCH 17/70] =removing unused imports --- packages/backend-common/src/service/lib/ServiceBuilderImpl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 899db248d6..4669c00e68 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -20,7 +20,6 @@ import cors from 'cors'; import express, { Router } from 'express'; import helmet from 'helmet'; import * as http from 'http'; -import * as https from 'https'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; From 15e334a584013ee22e9f5b4335427edaf723473f Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Thu, 20 Aug 2020 16:40:25 +0200 Subject: [PATCH 18/70] Release v0.1.1-alpha.19 --- CHANGELOG.md | 2 ++ microsite/package.json | 2 +- packages/app/package.json | 44 ++++++++++++------------- packages/backend-common/package.json | 10 +++--- packages/backend/package.json | 28 ++++++++-------- packages/catalog-model/package.json | 6 ++-- packages/cli-common/package.json | 4 +-- packages/cli/package.json | 8 ++--- packages/config-loader/package.json | 4 +-- packages/config/package.json | 2 +- packages/core-api/package.json | 10 +++--- packages/core/package.json | 12 +++---- packages/create-app/package.json | 4 +-- packages/dev-utils/package.json | 10 +++--- packages/docgen/package.json | 2 +- packages/storybook/package.json | 4 +-- packages/techdocs-cli/package.json | 4 +-- packages/test-utils-core/package.json | 2 +- packages/test-utils/package.json | 10 +++--- packages/theme/package.json | 4 +-- plugins/api-docs/package.json | 16 ++++----- plugins/auth-backend/package.json | 8 ++--- plugins/catalog-backend/package.json | 10 +++--- plugins/catalog/package.json | 26 +++++++-------- plugins/circleci/package.json | 10 +++--- plugins/explore/package.json | 12 +++---- plugins/github-actions/package.json | 16 ++++----- plugins/gitops-profiles/package.json | 10 +++--- plugins/graphiql/package.json | 12 +++---- plugins/graphql/package.json | 6 ++-- plugins/identity-backend/package.json | 6 ++-- plugins/jenkins/package.json | 12 +++---- plugins/lighthouse/package.json | 12 +++---- plugins/newrelic/package.json | 10 +++--- plugins/proxy-backend/package.json | 8 ++--- plugins/register-component/package.json | 14 ++++---- plugins/rollbar-backend/package.json | 8 ++--- plugins/rollbar/package.json | 12 +++---- plugins/scaffolder-backend/package.json | 10 +++--- plugins/scaffolder/package.json | 16 ++++----- plugins/sentry-backend/package.json | 6 ++-- plugins/sentry/package.json | 10 +++--- plugins/tech-radar/package.json | 12 +++---- plugins/techdocs-backend/package.json | 10 +++--- plugins/techdocs/package.json | 18 +++++----- plugins/welcome/package.json | 10 +++--- 46 files changed, 237 insertions(+), 235 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f03e3fa5..d295dc38e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re > Collect changes for the next release below +## v0.1.1-alpha.19 + ### @backstage/create-app - Many plugins have been added to the catalog and will for now be required to be added to separate apps as well. This will be solved as [#1536](https://github.com/spotify/backstage/issues/1536) gets sorted out, but for now you may need to install some plugins just to get pages to work. diff --git a/microsite/package.json b/microsite/package.json index dd8957245a..bcf28d7fe5 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "0.1.1-alpha.19", "name": "backstage-microsite", "license": "Apache-2.0", "private": true, diff --git a/packages/app/package.json b/packages/app/package.json index 126913b32a..ad4aa3524c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,29 +1,29 @@ { "name": "example-app", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": true, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-api-docs": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/plugin-circleci": "^0.1.1-alpha.18", - "@backstage/plugin-explore": "^0.1.1-alpha.18", - "@backstage/plugin-github-actions": "^0.1.1-alpha.18", - "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.18", - "@backstage/plugin-graphiql": "^0.1.1-alpha.18", - "@backstage/plugin-jenkins": "^0.1.1-alpha.18", - "@backstage/plugin-lighthouse": "^0.1.1-alpha.18", - "@backstage/plugin-newrelic": "^0.1.1-alpha.18", - "@backstage/plugin-register-component": "^0.1.1-alpha.18", - "@backstage/plugin-rollbar": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.18", - "@backstage/plugin-sentry": "^0.1.1-alpha.18", - "@backstage/plugin-tech-radar": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs": "^0.1.1-alpha.18", - "@backstage/plugin-welcome": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-api-docs": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/plugin-circleci": "^0.1.1-alpha.19", + "@backstage/plugin-explore": "^0.1.1-alpha.19", + "@backstage/plugin-github-actions": "^0.1.1-alpha.19", + "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.19", + "@backstage/plugin-graphiql": "^0.1.1-alpha.19", + "@backstage/plugin-jenkins": "^0.1.1-alpha.19", + "@backstage/plugin-lighthouse": "^0.1.1-alpha.19", + "@backstage/plugin-newrelic": "^0.1.1-alpha.19", + "@backstage/plugin-register-component": "^0.1.1-alpha.19", + "@backstage/plugin-rollbar": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.19", + "@backstage/plugin-sentry": "^0.1.1-alpha.19", + "@backstage/plugin-tech-radar": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs": "^0.1.1-alpha.19", + "@backstage/plugin-welcome": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 5cf2ee95e5..018c7616f4 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/config-loader": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/config-loader": "^0.1.1-alpha.19", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -54,7 +54,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/compression": "^1.7.0", "@types/http-errors": "^1.6.3", "@types/morgan": "^1.9.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index c80fbc7c38..99f0cf3ece 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,18 +18,18 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/plugin-auth-backend": "^0.1.1-alpha.18", - "@backstage/plugin-catalog-backend": "^0.1.1-alpha.18", - "@backstage/plugin-graphql-backend": "^0.1.1-alpha.18", - "@backstage/plugin-identity-backend": "^0.1.1-alpha.18", - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.18", - "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.18", - "@backstage/plugin-sentry-backend": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/plugin-auth-backend": "^0.1.1-alpha.19", + "@backstage/plugin-catalog-backend": "^0.1.1-alpha.19", + "@backstage/plugin-graphql-backend": "^0.1.1-alpha.19", + "@backstage/plugin-identity-backend": "^0.1.1-alpha.19", + "@backstage/plugin-proxy-backend": "^0.1.1-alpha.19", + "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.19", + "@backstage/plugin-sentry-backend": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.19", "@octokit/rest": "^18.0.0", "dockerode": "^3.2.0", "express": "^4.17.1", @@ -40,7 +40,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 248f2765e4..54b3d3a621 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", "@types/json-schema": "^7.0.5", "@types/yup": "^0.28.2", "json-schema": "^0.2.5", @@ -29,7 +29,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 092a2a5167..80cc665426 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "main": "src/index.ts", "types": "src/index.ts", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/jest": "^26.0.7", "@types/node": "^12.0.0" }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 3c9f85ac30..e5a8eafc81 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -29,9 +29,9 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/config-loader": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/config-loader": "^0.1.1-alpha.19", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index a2d7bbbc59..f5873c3c9d 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", "fs-extra": "^9.0.0", "yaml": "^1.9.2", "yup": "^0.29.1" diff --git a/packages/config/package.json b/packages/config/package.json index 9c8c81d31b..acbc690625 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 9ce1290a73..7b2087630e 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", @@ -41,8 +41,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/core/package.json b/packages/core/package.json index 3e33fec7a1..5b59ee3dff 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -54,8 +54,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 6a1a3b60b5..ae62bc518a 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", "chalk": "^4.0.0", "commander": "^4.1.1", "fs-extra": "^9.0.0", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 7eab350990..ba82447ea4 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 4b832afdfd..446f57f3dc 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,7 +1,7 @@ { "name": "docgen", "description": "Tool for generating API Documentation for itself", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": true, "homepage": "https://backstage.io", "repository": { diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 3a928b044e..8660c0e8d8 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "description": "Storybook build for core package", "private": true, "scripts": { @@ -14,7 +14,7 @@ ] }, "dependencies": { - "@backstage/theme": "^0.1.1-alpha.18" + "@backstage/theme": "^0.1.1-alpha.19" }, "devDependencies": { "@storybook/addon-actions": "^5.3.17", diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 2e5d1e3958..a5951fd770 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "CLI for running TechDocs locally.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "ext": "ts" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "commander": "^5.1.0", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 638513af5c..821a94b1aa 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils-core", "description": "Utilities to test Backstage core", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 35bbb7d0b0..e336835592 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/theme/package.json b/packages/theme/package.json index f4eafa58ff..f98a2cb762 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.9.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18" + "@backstage/cli": "^0.1.1-alpha.19" }, "files": [ "dist" diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index c8a6b5732d..342d0aa8ca 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@kyma-project/asyncapi-react": "^0.11.0", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.9.1", @@ -37,9 +37,9 @@ "swagger-ui-react": "^3.31.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 65111e599c..7771434750 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "body-parser": "^1.19.0", "compression": "^1.7.4", @@ -48,7 +48,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/jwt-decode": "2.2.1", diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 7dedbe62cd..5692ff992e 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "mock-data:local": "./scripts/mock-data-local.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -41,7 +41,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/lodash": "^4.14.151", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 44b3a7b7ee..1e10e91478 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,15 +21,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-api-docs": "^0.1.1-alpha.18", - "@backstage/plugin-github-actions": "^0.1.1-alpha.18", - "@backstage/plugin-jenkins": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.18", - "@backstage/plugin-sentry": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-api-docs": "^0.1.1-alpha.19", + "@backstage/plugin-github-actions": "^0.1.1-alpha.19", + "@backstage/plugin-jenkins": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.19", + "@backstage/plugin-sentry": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -42,9 +42,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 4fb7a28124..b70230364f 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index e7783675ae..9b729d7a82 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 8fcc4591d2..db35022fae 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,8 +39,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 5815386014..a2a7900d52 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 27437d0318..94a755bb17 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 02dc9d3608..f41a4540ec 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "apollo-server": "^2.16.0", "apollo-server-express": "^2.16.0", @@ -32,7 +32,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/identity-backend/package.json b/plugins/identity-backend/package.json index c5c3bffc9b..8b16d85827 100644 --- a/plugins/identity-backend/package.json +++ b/plugins/identity-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-identity-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "compression": "^1.7.4", "cors": "^2.8.5", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index dce577801e..2e8b89bea3 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index cbf4a77f7e..bcd34d32de 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 5b1b5d54ce..b6e066bbc0 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,8 +31,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 023fc81cf2..4a37ab8a45 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,8 +19,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -34,7 +34,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 02fd3fe0ed..c13121de66 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 4b4a45b9dd..a52abf91b9 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "axios": "^0.19.2", "camelcase-keys": "^6.2.2", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/supertest": "^2.0.8", "jest-fetch-mock": "^3.0.3", "supertest": "^4.0.2" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 1ca02cb528..5eeeacc505 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index fc956c9832..7112d0f49d 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@octokit/rest": "^18.0.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", @@ -43,7 +43,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@octokit/types": "^5.0.1", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index c0376a3594..8dec795c3d 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index e199dd2f1d..8850886493 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "axios": "^0.19.2", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 3a6cf54199..365a394331 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,8 +34,8 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index b2a7be7341..2e788e31e8 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 8a5a9fd10a..6a24ce3e8f 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "dockerode": "^3.2.1", @@ -35,7 +35,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/node-fetch": "^2.5.7", "supertest": "^4.0.2" }, diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 03f1d6053b..d5416c7329 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,8 +40,8 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 2c0c838de8..b832c5d37a 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From 613c06bdd6101b45ce3feb3329cde22796f631d9 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 12:11:03 +0200 Subject: [PATCH 19/70] Techdocs: Updates preparers to make it work in actual usecases (#1957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * attempt to add back react as a dep * Fixed failing test * fix(techdocs): Hopefully fixed tests * Fixed another failing test * Initial apiRef for techdocs storage * Cleaned up some code in reader * test(headertabs): add tests to HeaderTabs component * fix(headertabs): change tab mock id * fix(techdocs-generator): fix problem with macOS symlink to tmp folder * fix(lint): remove unused configApiRef * wip * Ongoing cleanups * Cleanups and fix tests * Initial github preparer. Doesn't work properly yet * Updated dir preparer to handle github managed-by-location and added github preparer * Removed old file * Fixed feedback and added better logging to techdocs build process * Updated create-app template Co-authored-by: Fredrik Adelöw Co-authored-by: Emma Indal --- packages/backend/src/plugins/techdocs.ts | 17 +- .../packages/backend/src/plugins/techdocs.ts | 6 +- .../documented-component.yaml | 2 +- plugins/techdocs-backend/package.json | 2 + .../src/service/router.test.ts | 3 +- .../techdocs-backend/src/service/router.ts | 18 +- .../src/service/standaloneServer.ts | 6 +- .../src/techdocs/stages/generate/techdocs.ts | 44 +++-- .../src/techdocs/stages/prepare/dir.test.ts | 7 +- .../src/techdocs/stages/prepare/dir.ts | 79 ++++++++- .../src/techdocs/stages/prepare/github.ts | 71 ++++++++ .../src/techdocs/stages/prepare/helpers.ts | 12 +- .../src/techdocs/stages/prepare/index.ts | 1 + .../src/techdocs/stages/prepare/preparers.ts | 11 +- .../src/techdocs/stages/publish/local.test.ts | 7 +- .../src/techdocs/stages/publish/local.ts | 21 ++- yarn.lock | 160 +++++++++++++++++- 17 files changed, 413 insertions(+), 54 deletions(-) create mode 100644 plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index 812a56cd9f..7364cc4d83 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -20,22 +20,27 @@ import { Preparers, Generators, LocalPublish, - TechdocsGenerator + TechdocsGenerator, + GithubPreparer, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; import Docker from 'dockerode'; -export default async function createPlugin({ logger, config }: PluginEnvironment) { +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const directoryPreparer = new DirectoryPreparer(); const preparers = new Preparers(); - + const githubPreparer = new GithubPreparer(logger); + const directoryPreparer = new DirectoryPreparer(logger); preparers.register('dir', directoryPreparer); + preparers.register('github', githubPreparer); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts index 8c5144f50c..a7b713fcb6 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts @@ -14,15 +14,15 @@ export default async function createPlugin({ config, }: PluginEnvironment) { const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const preparers = new Preparers(); preparers.register('dir', directoryPreparer); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml index d5a1214b15..045ff89013 100644 --- a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml +++ b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml @@ -4,7 +4,7 @@ metadata: name: documented-component description: A Service with TechDocs documentation annotations: - backstage.io/techdocs-ref: 'dir:./' + backstage.io/techdocs-ref: 'github:https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component' spec: type: service lifecycle: experimental diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 8a5a9fd10a..0ce09ba473 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -30,8 +30,10 @@ "express": "^4.17.1", "express-promise-router": "^3.0.3", "fs-extra": "^9.0.1", + "git-url-parse": "^11.1.3", "knex": "^0.21.1", "node-fetch": "^2.6.0", + "nodegit": "^0.27.0", "winston": "^3.2.1" }, "devDependencies": { diff --git a/plugins/techdocs-backend/src/service/router.test.ts b/plugins/techdocs-backend/src/service/router.test.ts index b28010e059..c266f98677 100644 --- a/plugins/techdocs-backend/src/service/router.test.ts +++ b/plugins/techdocs-backend/src/service/router.test.ts @@ -24,12 +24,13 @@ import { createRouter } from './router'; describe('createRouter', () => { let app: express.Express; + const logger = getVoidLogger(); beforeAll(async () => { const router = await createRouter({ preparers: new Preparers(), generators: new Generators(), - publisher: new LocalPublish(), + publisher: new LocalPublish(logger), logger: getVoidLogger(), dockerClient: new Docker(), config: ConfigReader.fromConfigs([]), diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index e11e3bb57f..f93d182edc 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -45,18 +45,34 @@ export async function createRouter({ publisher, config, dockerClient, + logger, }: RouterOptions): Promise { const router = Router(); + const getEntityId = (entity: Entity) => { + return `${entity.kind}:${entity.metadata.namespace ?? ''}:${ + entity.metadata.name + }`; + }; + const buildDocsForEntity = async (entity: Entity) => { const preparer = preparers.get(entity); const generator = generators.get(entity); + logger.info(`[TechDocs] Running preparer on entity ${getEntityId(entity)}`); + const preparedDir = await preparer.prepare(entity); + + logger.info( + `[TechDocs] Running generator on entity ${getEntityId(entity)}`, + ); const { resultDir } = await generator.run({ - directory: await preparer.prepare(entity), + directory: preparedDir, dockerClient, }); + logger.info( + `[TechDocs] Running publisher on entity ${getEntityId(entity)}`, + ); await publisher.publish({ entity, directory: resultDir, diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 76de973875..93233af4c0 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -41,14 +41,14 @@ export async function startStandaloneServer( logger.debug('Creating application...'); const preparers = new Preparers(); - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); preparers.register('dir', directoryPreparer); const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 32b811af78..96449a0bd6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -13,17 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { Logger } from 'winston'; + import { GeneratorBase, GeneratorRunOptions, GeneratorRunResult, } from './types'; import { runDockerContainer } from './helpers'; -import fs from 'fs-extra'; -import path from 'path'; -import os from 'os'; export class TechdocsGenerator implements GeneratorBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + public async run({ directory, logStream, @@ -36,18 +45,23 @@ export class TechdocsGenerator implements GeneratorBase { path.join(tmpdirResolvedPath, 'techdocs-tmp-'), ); - await runDockerContainer({ - imageName: 'spotify/techdocs', - args: ['build', '-d', '/result'], - logStream, - docsDir: directory, - resultDir, - dockerClient, - }); - - console.log( - `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, - ); + try { + await runDockerContainer({ + imageName: 'spotify/techdocs', + args: ['build', '-d', '/result'], + logStream, + docsDir: directory, + resultDir, + dockerClient, + }); + this.logger.info( + `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, + ); + } catch (error) { + this.logger.debug( + `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`, + ); + } return { resultDir }; } diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts index 8a99a8b59a..fed7a0e107 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts @@ -14,6 +14,9 @@ * limitations under the License. */ import { DirectoryPreparer } from './dir'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); const createMockEntity = (annotations: {}) => { return { @@ -30,7 +33,7 @@ const createMockEntity = (annotations: {}) => { describe('directory preparer', () => { it('should merge managed-by-location and techdocs-ref when techdocs-ref is relative', async () => { - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const mockEntity = createMockEntity({ 'backstage.io/managed-by-location': @@ -44,7 +47,7 @@ describe('directory preparer', () => { }); it('should merge managed-by-location and techdocs-ref when techdocs-ref is absolute', async () => { - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const mockEntity = createMockEntity({ 'backstage.io/managed-by-location': diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts index e61a3e48ac..1560c2ea8e 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts @@ -13,26 +13,95 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import fs from 'fs-extra'; +import os from 'os'; import { PreparerBase } from './types'; import { Entity } from '@backstage/catalog-model'; import path from 'path'; import { parseReferenceAnnotation } from './helpers'; +import { InputError } from '@backstage/backend-common'; +import { Clone } from 'nodegit'; +import parseGitUrl from 'git-url-parse'; +import { Logger } from 'winston'; export class DirectoryPreparer implements PreparerBase { - prepare(entity: Entity): Promise { - const { location: managedByLocation } = parseReferenceAnnotation( + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + + private async cloneGithubRepo(entity: Entity) { + const { type, target } = parseReferenceAnnotation( 'backstage.io/managed-by-location', entity, ); - const { location: techdocsLocation } = parseReferenceAnnotation( + + if (type !== 'github') { + throw new InputError(`Wrong target type: ${type}, should be 'github'`); + } + + const parsedGitLocation = parseGitUrl(target); + const repositoryTmpPath = path.join( + os.tmpdir(), + 'backstage-repo', + parsedGitLocation.source, + parsedGitLocation.owner, + parsedGitLocation.name, + parsedGitLocation.ref, + ); + if (fs.existsSync(repositoryTmpPath)) { + return repositoryTmpPath; + } + const repositoryCheckoutUrl = parsedGitLocation.toString('https'); + + this.logger.debug( + `[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`, + ); + + fs.mkdirSync(repositoryTmpPath, { recursive: true }); + await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {}); + + return repositoryTmpPath; + } + + private async resolveManagedByLocationToDir(entity: Entity) { + const { type, target } = parseReferenceAnnotation( + 'backstage.io/managed-by-location', + entity, + ); + + this.logger.debug( + `[TechDocs] Building docs for entity with type 'dir' and managed-by-location '${type}'`, + ); + switch (type) { + case 'github': { + const parsedGitLocation = parseGitUrl(target); + const repoLocation = await this.cloneGithubRepo(entity); + + return path.dirname( + path.join(repoLocation, parsedGitLocation.filepath), + ); + } + case 'file': + return path.dirname(target); + default: + throw new InputError(`Unable to resolve location type ${type}`); + } + } + + async prepare(entity: Entity): Promise { + const { target } = parseReferenceAnnotation( 'backstage.io/techdocs-ref', entity, ); - const managedByLocationDirectory = path.dirname(managedByLocation); + const managedByLocationDirectory = await this.resolveManagedByLocationToDir( + entity, + ); return new Promise(resolve => { - resolve(path.resolve(managedByLocationDirectory, techdocsLocation)); + resolve(path.resolve(managedByLocationDirectory, target)); }); } } diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts new file mode 100644 index 0000000000..27ee277f02 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { Entity } from '@backstage/catalog-model'; +import { InputError } from '@backstage/backend-common'; +import { PreparerBase } from './types'; +import parseGitUrl from 'git-url-parse'; +import { Clone } from 'nodegit'; +import { parseReferenceAnnotation } from './helpers'; +import { Logger } from 'winston'; + +export class GithubPreparer implements PreparerBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + + async prepare(entity: Entity): Promise { + const { type, target } = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', + entity, + ); + + if (type !== 'github') { + throw new InputError(`Wrong target type: ${type}, should be 'github'`); + } + + const parsedGitLocation = parseGitUrl(target); + const repositoryTmpPath = path.join( + os.tmpdir(), + 'backstage-repo', + parsedGitLocation.source, + parsedGitLocation.owner, + parsedGitLocation.name, + parsedGitLocation.ref, + ); + + if (fs.existsSync(repositoryTmpPath)) { + this.logger.debug( + `[TechDocs] Found repository already checked out at ${repositoryTmpPath}`, + ); + return path.join(repositoryTmpPath, parsedGitLocation.filepath); + } + const repositoryCheckoutUrl = parsedGitLocation.toString('https'); + + this.logger.debug( + `[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`, + ); + + fs.mkdirSync(repositoryTmpPath, { recursive: true }); + await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {}); + + return path.join(repositoryTmpPath, parsedGitLocation.filepath); + } +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts index 1fd86805ca..3b3388d58d 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts @@ -18,8 +18,8 @@ import { InputError } from '@backstage/backend-common'; import { RemoteProtocol } from './types'; export type ParsedLocationAnnotation = { - protocol: RemoteProtocol; - location: string; + type: RemoteProtocol; + target: string; }; export const parseReferenceAnnotation = ( @@ -36,19 +36,19 @@ export const parseReferenceAnnotation = ( // split on the first colon for the protocol and the rest after the first split // is the location. - const [protocol, location] = annotation.split(/:(.+)/) as [ + const [type, target] = annotation.split(/:(.+)/) as [ RemoteProtocol?, string?, ]; - if (!protocol || !location) { + if (!type || !target) { throw new InputError( `Failure to parse either protocol or location for entity: ${entity.metadata.name}`, ); } return { - protocol, - location, + type, + target, }; }; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts index 9f928e7413..535b56f327 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ export { DirectoryPreparer } from './dir'; +export { GithubPreparer } from './github'; export { Preparers } from './preparers'; export type { PreparerBuilder } from './types'; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts index 1d5b689d8b..1c8fadd145 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts @@ -26,13 +26,16 @@ export class Preparers implements PreparerBuilder { } get(entity: Entity): PreparerBase { - const { protocol } = parseReferenceAnnotation('backstage.io/techdocs-ref', entity); - const preparer = this.preparerMap.get(protocol); + const { type } = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', + entity, + ); + const preparer = this.preparerMap.get(type); if (!preparer) { - throw new Error(`No preparer registered for type: "${protocol}"`); + throw new Error(`No preparer registered for type: "${type}"`); } return preparer; } -} \ No newline at end of file +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts index 1ed8be3898..55e0a547e9 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { LocalPublish } from './local'; import fs from 'fs-extra'; import path from 'path'; +import { getVoidLogger } from '@backstage/backend-common'; +import { LocalPublish } from './local'; const createMockEntity = (annotations = {}) => { return { @@ -30,9 +31,11 @@ const createMockEntity = (annotations = {}) => { }; }; +const logger = getVoidLogger(); + describe('local publisher', () => { it('should publish generated documentation dir', async () => { - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const mockEntity = createMockEntity(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index 73a7d5a758..be9e1876d8 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -13,12 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { PublisherBase } from './types'; -import { Entity } from '@backstage/catalog-model'; -import path from 'path'; import fs from 'fs-extra'; +import path from 'path'; +import { Logger } from 'winston'; +import { Entity } from '@backstage/catalog-model'; +import { PublisherBase } from './types'; export class LocalPublish implements PublisherBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + publish({ entity, directory, @@ -37,16 +44,22 @@ export class LocalPublish implements PublisherBase { '../../../../static/docs/', entity.kind, entityNamespace, - entity.metadata.name + entity.metadata.name, ); if (!fs.existsSync(publishDir)) { + this.logger.info( + `[TechDocs]: Could not find ${publishDir}, creates the directory.`, + ); fs.mkdirSync(publishDir, { recursive: true }); } return new Promise((resolve, reject) => { fs.copy(directory, publishDir, err => { if (err) { + this.logger.debug( + `[TechDocs]: Failed to copy docs from ${directory} to ${publishDir}`, + ); reject(err); } diff --git a/yarn.lock b/yarn.lock index c3527d6df6..f9e0a21696 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,6 +3419,11 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^2.0.0": + version "2.1.1" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1" + integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg== + "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -4302,6 +4307,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.0": + version "4.0.5" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@testing-library/cypress@^6.0.0": version "6.0.0" resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce" @@ -4467,6 +4479,16 @@ "@types/connect" "*" "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/cheerio@^0.22.8": version "0.22.21" resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" @@ -4739,6 +4761,11 @@ resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ== +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/http-errors@^1.6.3": version "1.8.0" resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69" @@ -4833,6 +4860,13 @@ resolved "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== +"@types/keyv@*", "@types/keyv@^3.1.1": + version "3.1.1" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/koa-compose@*": version "3.2.5" resolved "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" @@ -5150,6 +5184,13 @@ dependencies: "@types/node" "*" +"@types/responselike@*": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/rollup-plugin-peer-deps-external@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@types/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.0.tgz#eae7d8b9d27fa037f5bcaded24e389f85b81973c" @@ -7389,6 +7430,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz#87be64a18b925234875e10a9bb1ebca4adce6b38" + integrity sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg== + dependencies: + "@types/keyv" "^3.1.1" + keyv "^4.0.0" + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -7415,6 +7464,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + cachedir@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -9100,6 +9162,13 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +decompress-response@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" + integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== + dependencies: + mimic-response "^2.0.0" + decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" @@ -9215,6 +9284,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -11630,6 +11704,13 @@ git-url-parse@^11.1.2: dependencies: git-up "^4.0.0" +git-url-parse@^11.1.3: + version "11.1.3" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.3.tgz#03625b6fc09905e9ad1da7bb2b84be1bf9123143" + integrity sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA== + dependencies: + git-up "^4.0.0" + gitconfiglocal@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" @@ -11853,6 +11934,27 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +got@^10.7.0: + version "10.7.0" + resolved "https://registry.npmjs.org/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f" + integrity sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg== + dependencies: + "@sindresorhus/is" "^2.0.0" + "@szmarczak/http-timer" "^4.0.0" + "@types/cacheable-request" "^6.0.1" + cacheable-lookup "^2.0.0" + cacheable-request "^7.0.1" + decompress-response "^5.0.0" + duplexer3 "^0.1.4" + get-stream "^5.0.0" + lowercase-keys "^2.0.0" + mimic-response "^2.1.0" + p-cancelable "^2.0.0" + p-event "^4.0.0" + responselike "^2.0.0" + to-readable-stream "^2.0.0" + type-fest "^0.10.0" + got@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -14292,6 +14394,11 @@ json-buffer@3.0.0: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -14547,6 +14654,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6" + integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -15733,7 +15847,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^2.0.0: +mimic-response@^2.0.0, mimic-response@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== @@ -16323,6 +16437,21 @@ nodegit@0.26.5: request-promise-native "^1.0.5" tar-fs "^1.16.3" +nodegit@^0.27.0: + version "0.27.0" + resolved "https://registry.npmjs.org/nodegit/-/nodegit-0.27.0.tgz#4e8cc236f60e1c97324a5acff99056fe116a6ebe" + integrity sha512-E9K4gPjWiA0b3Tx5lfWCzG7Cvodi2idl3V5UD2fZrOrHikIfrN7Fc2kWLtMUqqomyoToYJLeIC8IV7xb1CYRLA== + dependencies: + fs-extra "^7.0.0" + got "^10.7.0" + json5 "^2.1.0" + lodash "^4.17.14" + nan "^2.14.0" + node-gyp "^4.0.0" + node-pre-gyp "^0.13.0" + ramda "^0.25.0" + tar-fs "^1.16.3" + nodemon@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" @@ -16870,6 +16999,11 @@ p-cancelable@^1.0.0: resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -16889,6 +17023,13 @@ p-event@^2.1.0: dependencies: p-timeout "^2.0.1" +p-event@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -19745,6 +19886,13 @@ responselike@1.0.2, responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -21906,6 +22054,11 @@ to-readable-stream@^1.0.0: resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +to-readable-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" + integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -22204,6 +22357,11 @@ type-detect@4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" + integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== + type-fest@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" From b9683dc2369b5fde8a2d8bf54703d748be081f62 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Fri, 21 Aug 2020 12:27:03 +0200 Subject: [PATCH 20/70] Commit lock file --- yarn.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index c3527d6df6..140bd5dcdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1544,6 +1544,42 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@backstage/core@0.1.1-alpha.18": + version "0.1.1-alpha.18" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.1.1-alpha.18.tgz#a8384b6fa65af17ee76166f2e0e4922356856ec1" + integrity sha512-7I0eaTfF30xyX4CD/bFUnXXy22zQzQbqSFFYIGLm5j/XFi3IRb6WANoFMs1hvB/BPI/rojX9oN51PfuHBS6xHg== + dependencies: + "@backstage/config" "^0.1.1-alpha.18" + "@backstage/core-api" "^0.1.1-alpha.18" + "@backstage/theme" "^0.1.1-alpha.18" + "@material-ui/core" "^4.9.1" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + lodash "^4.17.15" + material-table "1.62.x" + prop-types "^15.7.2" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^5.7.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.2.1" + react-use "^15.3.3" + +"@backstage/theme@0.1.1-alpha.18": + version "0.1.1-alpha.18" + resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.1.1-alpha.18.tgz#40adb3e798054d75ccf57c81e5fe3cb6e32cfac5" + integrity sha512-dDTUGH/fxRGtwnR6NCK44ojlfNyFQJKVlax2CNIB/JA43+2c5G2ipDvJ8MhxDeTwwNeO+HJj1AF0AqoJcc1QdQ== + dependencies: + "@material-ui/core" "^4.9.1" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -15446,6 +15482,22 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" +material-table@1.62.x: + version "1.62.0" + resolved "https://registry.npmjs.org/material-table/-/material-table-1.62.0.tgz#117793ebf16ab0fccbb6f8a670d849a7be0b5995" + integrity sha512-+3tnk32lXtkXeKM7k/hZ82jpSzlXU5CsWXqJHq4Tl0Un7ycjK2Kef6EMPqeE3i58vKNqbIvbrFf/ESH0D/Qwig== + dependencies: + "@date-io/date-fns" "^1.1.0" + "@material-ui/pickers" "^3.2.2" + classnames "^2.2.6" + date-fns "^2.0.0-alpha.27" + debounce "^1.2.0" + fast-deep-equal "2.0.1" + filefy "0.1.10" + prop-types "^15.6.2" + react-beautiful-dnd "^13.0.0" + react-double-scrollbar "0.0.15" + material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -19070,7 +19122,7 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.5.1: +react-syntax-highlighter@^13.2.1, react-syntax-highlighter@^13.5.1: version "13.5.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== From 4d17c1fd94666c8114899d357c044755107d6b60 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 21 Aug 2020 13:24:43 +0200 Subject: [PATCH 21/70] cli: fix jest transform pattern termination --- packages/cli/config/jest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 77d3a84e82..d95dd21489 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -37,8 +37,8 @@ async function getConfig() { // TODO: jest is working on module support, it's possible that we can remove this in the future transform: { '\\.esm\\.js$': require.resolve('jest-esm-transformer'), - '\\.(js|jsx|ts|tsx)': require.resolve('ts-jest'), - '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)': require.resolve( + '\\.(js|jsx|ts|tsx)$': require.resolve('ts-jest'), + '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve( './jestFileTransform.js', ), }, From f43063d463f30bf66c021b3c95e76fe8f50f4d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 21 Aug 2020 13:28:54 +0200 Subject: [PATCH 22/70] fix dependency and lockfile --- packages/app/package.json | 2 +- yarn.lock | 66 +++++---------------------------------- 2 files changed, 8 insertions(+), 60 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index ad4aa3524c..d6598b9954 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -28,7 +28,7 @@ "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", "@roadiehq/backstage-plugin-github-pull-requests": "0.3.0", - "@roadiehq/backstage-plugin-travis-ci": "^0.1.3", + "@roadiehq/backstage-plugin-travis-ci": "^0.1.4", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/yarn.lock b/yarn.lock index 140bd5dcdc..ec6343107a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1544,42 +1544,6 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@backstage/core@0.1.1-alpha.18": - version "0.1.1-alpha.18" - resolved "https://registry.npmjs.org/@backstage/core/-/core-0.1.1-alpha.18.tgz#a8384b6fa65af17ee76166f2e0e4922356856ec1" - integrity sha512-7I0eaTfF30xyX4CD/bFUnXXy22zQzQbqSFFYIGLm5j/XFi3IRb6WANoFMs1hvB/BPI/rojX9oN51PfuHBS6xHg== - dependencies: - "@backstage/config" "^0.1.1-alpha.18" - "@backstage/core-api" "^0.1.1-alpha.18" - "@backstage/theme" "^0.1.1-alpha.18" - "@material-ui/core" "^4.9.1" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - classnames "^2.2.6" - clsx "^1.1.0" - lodash "^4.17.15" - material-table "1.62.x" - prop-types "^15.7.2" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^5.7.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^13.2.1" - react-use "^15.3.3" - -"@backstage/theme@0.1.1-alpha.18": - version "0.1.1-alpha.18" - resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.1.1-alpha.18.tgz#40adb3e798054d75ccf57c81e5fe3cb6e32cfac5" - integrity sha512-dDTUGH/fxRGtwnR6NCK44ojlfNyFQJKVlax2CNIB/JA43+2c5G2ipDvJ8MhxDeTwwNeO+HJj1AF0AqoJcc1QdQ== - dependencies: - "@material-ui/core" "^4.9.1" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3367,13 +3331,13 @@ react-router-dom "6.0.0-beta.0" react-use "^15.3.3" -"@roadiehq/backstage-plugin-travis-ci@^0.1.3": - version "0.1.3" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.1.3.tgz#597882bb6db119b9c8970f15cc506c5d70ba2c41" - integrity sha512-xTnc71VtQSp7qfiE/p92U2/FEAvsG32BHIELebYZGCIt+ZqY/fSiXZSGsOXFDlg9adTu0e8ro6HWnknXCdo2pw== +"@roadiehq/backstage-plugin-travis-ci@^0.1.4": + version "0.1.4" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.1.4.tgz#ddbb2ba5b9d47f474462ecbc8c769759578e8526" + integrity sha512-T1eJ0huhwZcRHjrDKWoAzlKBv8UbhXgrvE7wuHD7dCcmfQdln0jbn7cskOpTnVmrzGXWKQ6mDyKX3LfdEPYbAw== dependencies: - "@backstage/core" "0.1.1-alpha.18" - "@backstage/theme" "0.1.1-alpha.18" + "@backstage/core" "^0.1.1-alpha.18" + "@backstage/theme" "^0.1.1-alpha.18" "@material-ui/core" "^4.9.1" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" @@ -15482,22 +15446,6 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" -material-table@1.62.x: - version "1.62.0" - resolved "https://registry.npmjs.org/material-table/-/material-table-1.62.0.tgz#117793ebf16ab0fccbb6f8a670d849a7be0b5995" - integrity sha512-+3tnk32lXtkXeKM7k/hZ82jpSzlXU5CsWXqJHq4Tl0Un7ycjK2Kef6EMPqeE3i58vKNqbIvbrFf/ESH0D/Qwig== - dependencies: - "@date-io/date-fns" "^1.1.0" - "@material-ui/pickers" "^3.2.2" - classnames "^2.2.6" - date-fns "^2.0.0-alpha.27" - debounce "^1.2.0" - fast-deep-equal "2.0.1" - filefy "0.1.10" - prop-types "^15.6.2" - react-beautiful-dnd "^13.0.0" - react-double-scrollbar "0.0.15" - material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -19122,7 +19070,7 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.2.1, react-syntax-highlighter@^13.5.1: +react-syntax-highlighter@^13.5.1: version "13.5.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== From 9b655c8f713c031092ee744922347b1112527205 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 14:47:17 +0200 Subject: [PATCH 23/70] chore(deps): bump @material-icons/font from 1.0.3 to 1.0.4 (#2063) Bumps [@material-icons/font](https://github.com/material-icons/material-icons) from 1.0.3 to 1.0.4. - [Release notes](https://github.com/material-icons/material-icons/releases) - [Commits](https://github.com/material-icons/material-icons/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4329ae46ca..38855bb50d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2874,9 +2874,9 @@ write-file-atomic "^2.3.0" "@material-icons/font@^1.0.2": - version "1.0.3" - resolved "https://registry.npmjs.org/@material-icons/font/-/font-1.0.3.tgz#f722e5a69a03f20ef47d015cb69420bebeeaabe5" - integrity sha512-aIRd0Z9b/HJ/O24KOaP7dNsXypMnXhpWrpLVYvQB/JesVqXYSbioYND200sR+C14a0LSCp+qWnWCnSXmN1hWGw== + version "1.0.4" + resolved "https://registry.npmjs.org/@material-icons/font/-/font-1.0.4.tgz#4bfc59dcaba90f6d71e4aeffd1ebb2ea6119c844" + integrity sha512-tt80HS8dDtx/jj6fXWi6CycCZWC1AY3YOXjHwxShwe/jrW/fRVdw0C9JOGo0ij5/7y+uG0Rp0c4plu7J4Cq7EA== "@material-ui/core@^4.9.1": version "4.9.7" From 307106588491ffcd1008f07359d672168793a091 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 14:47:45 +0200 Subject: [PATCH 24/70] chore(deps): bump swagger-ui-react from 3.31.1 to 3.32.4 (#2059) Bumps [swagger-ui-react](https://github.com/swagger-api/swagger-ui) from 3.31.1 to 3.32.4. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v3.31.1...v3.32.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 168 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 64 deletions(-) diff --git a/yarn.lock b/yarn.lock index 38855bb50d..06324a67a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1415,7 +1415,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime-corejs2@^7.10.4": +"@babel/runtime-corejs2@^7.10.4", "@babel/runtime-corejs2@^7.8.7": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.11.2.tgz#700a03945ebad0d31ba6690fc8a6bcc9040faa47" integrity sha512-AC/ciV28adSSpEkBglONBWq4/Lvm6GAZuxIoyVtsnUpZMl0bxLtoChEnYAkP+47KyOCayZanojtflUEUJtR/6Q== @@ -1423,14 +1423,6 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime-corejs2@^7.8.7": - version "7.10.3" - resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.10.3.tgz#81bc99a96bfcb6db3f0dcf73fdc577cc554d341b" - integrity sha512-enKvnR/kKFbZFgXYo165wtSA5OeiTlgsnU4jV3vpKRhfWUJjLS6dfVcjIPeRcgJbgEgdgu0I+UyBWqu6c0GumQ== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.4" - "@babel/runtime-corejs3@^7.10.2": version "7.10.3" resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz#931ed6941d3954924a7aa967ee440e60c507b91a" @@ -8484,6 +8476,11 @@ core-js-pure@^3.0.0, core-js-pure@^3.0.1: resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= + core-js@^2.4.0, core-js@^2.5.7, core-js@^2.6.11, core-js@^2.6.5: version "2.6.11" resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" @@ -8567,6 +8564,15 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-class@^15.5.1: + version "15.6.3" + resolved "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" + integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + object-assign "^4.1.1" + create-react-context@0.3.0, create-react-context@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" @@ -10919,6 +10925,19 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fbjs@^0.8.9: + version "0.8.17" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -12377,11 +12396,6 @@ highlight.js@~9.13.0: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== -highlight.js@~9.15.0, highlight.js@~9.15.1: - version "9.15.10" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" - integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== - history@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" @@ -13085,7 +13099,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.0.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -13579,7 +13593,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -13720,6 +13734,14 @@ isobject@^4.0.0: resolved "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + isomorphic-form-data@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz#9f6adf1c4c61ae3aefd8f110ab60fb9b143d6cec" @@ -14271,15 +14293,7 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1, js-yaml@^3.8.3: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^3.14.0, js-yaml@^3.8.1: +js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.8.1, js-yaml@^3.8.3: version "3.14.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -15058,7 +15072,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.11: +lodash-es@^4.17.11, lodash-es@^4.2.1: version "4.17.15" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -15302,7 +15316,7 @@ longest@^1.0.0: resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -15339,14 +15353,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lowlight@1.12.1: - version "1.12.1" - resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz#014acf8dd73a370e02ff1cc61debcde3bb1681eb" - integrity sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w== - dependencies: - fault "^1.0.2" - highlight.js "~9.15.0" - lowlight@^1.14.0: version "1.14.0" resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.14.0.tgz#83ebc143fec0f9e6c0d3deffe01be129ce56b108" @@ -16261,6 +16267,14 @@ node-fetch@2.6.0, node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0, node- resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + node-forge@0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -18439,6 +18453,13 @@ promise.series@^0.2.0: resolved "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + promise@^8.0.3: version "8.1.0" resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" @@ -19052,7 +19073,7 @@ react-inspector@^4.0.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -19072,7 +19093,7 @@ react-lazylog@^4.5.2: text-encoding-utf-8 "^1.0.1" whatwg-fetch "^2.0.4" -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -19121,18 +19142,17 @@ react-popper@^1.3.6: typed-styles "^0.0.7" warning "^4.0.2" -react-redux@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== +react-redux@=4.4.10: + version "4.4.10" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz#ad57bd1db00c2d0aa7db992b360ce63dd0b80ec5" + integrity sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg== dependencies: - "@babel/runtime" "^7.1.2" + create-react-class "^15.5.1" hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" + invariant "^2.0.0" + lodash "^4.17.11" + loose-envify "^1.4.0" + prop-types "^15.7.2" react-redux@^7.1.1: version "7.2.0" @@ -19189,16 +19209,16 @@ react-string-replace@^0.4.1: dependencies: lodash "^4.17.4" -react-syntax-highlighter@=12.2.1: - version "12.2.1" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz#14d78352da1c1c3f93c6698b70ec7c706b83493e" - integrity sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA== +react-syntax-highlighter@=13.5.0: + version "13.5.0" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.0.tgz#c0f3fd826a52b2a6ce5786d4ce60c3e0665b65c0" + integrity sha512-2nKo8spFxe9shcjbdUiqxkrf/IMDqKUZLx7JVIxEJ17P+fYFGL4CRsZZC66UPeQ2o/f29eKu31CrkKGCK1RHuA== dependencies: "@babel/runtime" "^7.3.1" - highlight.js "~9.15.1" - lowlight "1.12.1" - prismjs "^1.8.4" - refractor "^2.4.1" + highlight.js "^10.1.1" + lowlight "^1.14.0" + prismjs "^1.21.0" + refractor "^3.1.0" react-syntax-highlighter@^11.0.2: version "11.0.2" @@ -19535,7 +19555,17 @@ redux-immutable@3.1.0: dependencies: immutable "^3.8.1" -redux@^4.0.4, redux@^4.0.5: +redux@=3.7.2: + version "3.7.2" + resolved "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.3" + +redux@^4.0.4: version "4.0.5" resolved "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -20411,7 +20441,7 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= @@ -21568,9 +21598,9 @@ swagger-client@=3.10.12: url "~0.11.0" swagger-ui-react@^3.31.1: - version "3.31.1" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.31.1.tgz#1770cfa1dc390f79887d641ea35ee654f4bf7d80" - integrity sha512-bGpNzizMT/7N4LTdw6yx+t3rpsEoalrn0VIfhUzv9zXVrUTl+3AKPLHpLBr0r4NvzvaZLrYuSjkatnLfngJ4vg== + version "3.32.4" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.32.4.tgz#cc2cef0e2b9faff7c0dab111e9b4b9c87d0a01a2" + integrity sha512-AbdeBVIV6oM4pXKEsGY2Ijs1ySmdVYP4cHdZYu0kWghxKVqskocsfB+SsvRTh6mOxdZ2YNAEhc7RMdSqaWnGgw== dependencies: "@babel/runtime-corejs2" "^7.10.4" "@braintree/sanitize-url" "^4.0.0" @@ -21596,9 +21626,9 @@ swagger-ui-react@^3.31.1: react-immutable-pure-component "^1.1.1" react-inspector "^2.3.0" react-motion "^0.5.2" - react-redux "^5.1.2" - react-syntax-highlighter "=12.2.1" - redux "^4.0.5" + react-redux "=4.4.10" + react-syntax-highlighter "=13.5.0" + redux "=3.7.2" redux-immutable "3.1.0" remarkable "^2.0.1" reselect "^4.0.0" @@ -21616,7 +21646,7 @@ swr@^0.3.0: dependencies: fast-deep-equal "2.0.1" -symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -22422,6 +22452,11 @@ typescript@^3.9.3: resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +ua-parser-js@^0.7.18: + version "0.7.21" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -23202,6 +23237,11 @@ whatwg-fetch@3.0.0, whatwg-fetch@^3.0.0: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== +whatwg-fetch@>=0.10.0: + version "3.4.0" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz#e11de14f4878f773fbebcde8871b2c0699af8b30" + integrity sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ== + whatwg-fetch@^2.0.0, whatwg-fetch@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" From c6c3c94c18f73d72de35d3f1e5ac258b5a2bb5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Fri, 21 Aug 2020 14:48:16 +0200 Subject: [PATCH 25/70] Camelcase GitHub and GitLab (#2034) * Camelcase GitHub and GitLab * GitLab * Code GitLab * GitHub in code * Fix auth naming * Update docs/features/software-templates/adding-templates.md Co-authored-by: Raghunandan Balachandran * Update docs/features/software-templates/extending/create-your-own-preparer.md Co-authored-by: Raghunandan Balachandran Co-authored-by: Raghunandan Balachandran --- docs/features/software-templates/adding-templates.md | 2 +- .../extending/create-your-own-preparer.md | 2 +- docs/features/software-templates/index.md | 2 +- docs/features/software-templates/installation.md | 2 +- docs/reference/utility-apis/README.md | 4 ++-- microsite/blog/2020-03-18-what-is-backstage.md | 2 +- packages/app/src/identityProviders.ts | 8 ++++---- packages/core-api/src/apis/definitions/auth.ts | 8 ++++---- packages/core/src/layout/Sidebar/UserSettings.tsx | 4 ++-- plugins/auth-backend/src/providers/github/provider.ts | 2 +- .../ingestion/processors/GitlabApiReaderProcessor.test.ts | 2 +- .../src/ingestion/processors/GitlabApiReaderProcessor.ts | 4 ++-- .../src/ingestion/processors/GitlabReaderProcessor.ts | 2 +- plugins/github-actions/src/api/GithubActionsApi.ts | 2 +- plugins/graphiql/src/lib/api/GraphQLEndpoints.ts | 2 +- .../src/scaffolder/stages/publish/github.test.ts | 2 +- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 2ec4bdd6f3..b0405cab79 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -54,7 +54,7 @@ contains more information about the required fields. Once we have a `template.yaml` ready, we can then add it to the service catalog for use by the scaffolder. -Currently the catalog supports loading definitions from Github + Local Files. To +Currently the catalog supports loading definitions from GitHub + Local Files. To load from other places, not only will there need to be another preparer, but the support to load the location will also need to be added to the Catalog. diff --git a/docs/features/software-templates/extending/create-your-own-preparer.md b/docs/features/software-templates/extending/create-your-own-preparer.md index 739c516658..0b3fb31323 100644 --- a/docs/features/software-templates/extending/create-your-own-preparer.md +++ b/docs/features/software-templates/extending/create-your-own-preparer.md @@ -54,7 +54,7 @@ The `protocol` is set on the when added to the service catalog. You can see more about this `PreparerKey` here in [Register your own template](../adding-templates.md) -**note:** Currently the catalog supports loading definitions from Github + Local +**note:** Currently the catalog supports loading definitions from GitHub + Local Files, which translate into the two `PreparerKeys` `file` and `github`. To load from other places, not only will there need to be another preparer, but the support to load the location will also need to be added to the Catalog. diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md index d9a86ee7aa..503fbb7a3d 100644 --- a/docs/features/software-templates/index.md +++ b/docs/features/software-templates/index.md @@ -34,7 +34,7 @@ internally. After filling in these variables, you'll get some more fields to fill out which are required for backstage usage. The owner, which is a `user` in the backstage -system, and the `storePath` which right now must be a Github Organisation and a +system, and the `storePath` which right now must be a GitHub Organisation and a non-existing github repository name in the format `organisation/reponame`. ![Enter backstage vars](../../assets/software-templates/template-picked-2.png) diff --git a/docs/features/software-templates/installation.md b/docs/features/software-templates/installation.md index 0508d1f3f7..73b4f20359 100644 --- a/docs/features/software-templates/installation.md +++ b/docs/features/software-templates/installation.md @@ -109,7 +109,7 @@ export default async function createPlugin({ logger }: PluginEnvironment) { preparers.register('file', filePreparer); preparers.register('github', githubPreparer); - // Create Github client with your access token from environment variables + // Create GitHub client with your access token from environment variables const githubClient = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN }); const publisher = new GithubPublisher({ client: githubClient }); diff --git a/docs/reference/utility-apis/README.md b/docs/reference/utility-apis/README.md index fbfbbc475e..521628fd33 100644 --- a/docs/reference/utility-apis/README.md +++ b/docs/reference/utility-apis/README.md @@ -55,7 +55,7 @@ ApiRef: ## githubAuth -Provides authentication towards Github APIs +Provides authentication towards GitHub APIs Implemented types: [OAuthApi](./OAuthApi.md), [ProfileInfoApi](./ProfileInfoApi.md), @@ -67,7 +67,7 @@ ApiRef: ## gitlabAuth -Provides authentication towards Gitlab APIs +Provides authentication towards GitLab APIs Implemented types: [OAuthApi](./OAuthApi.md), [ProfileInfoApi](./ProfileInfoApi.md), diff --git a/microsite/blog/2020-03-18-what-is-backstage.md b/microsite/blog/2020-03-18-what-is-backstage.md index 942adc607f..8b0a0fcbe6 100644 --- a/microsite/blog/2020-03-18-what-is-backstage.md +++ b/microsite/blog/2020-03-18-what-is-backstage.md @@ -13,7 +13,7 @@ Two days ago, we released the open source version of [Backstage](https://backsta ## What’s the big infrastructure problem? -As companies grow, their infrastructure systems get messier. Consider a team that wants to deploy something to the cloud. While Spotify has many awesome engineers, not every engineer is well-versed in our chosen cloud-provider tooling. Yet everyone is required to know and understand Terraform, GCP/AWS/Azure CLIs, Gitlab CI, Prometheus, Kubernetes, Docker, various monitoring and alerting tools, and much, much more. Once other resources come into play (databases, queueing, etc.), each engineer requires even more tools and domain-specific knowledge (or “disciplines”), from backend to machine learning, to mobile and data. +As companies grow, their infrastructure systems get messier. Consider a team that wants to deploy something to the cloud. While Spotify has many awesome engineers, not every engineer is well-versed in our chosen cloud-provider tooling. Yet everyone is required to know and understand Terraform, GCP/AWS/Azure CLIs, GitLab CI, Prometheus, Kubernetes, Docker, various monitoring and alerting tools, and much, much more. Once other resources come into play (databases, queueing, etc.), each engineer requires even more tools and domain-specific knowledge (or “disciplines”), from backend to machine learning, to mobile and data. ## What’s the fix? diff --git a/packages/app/src/identityProviders.ts b/packages/app/src/identityProviders.ts index e80b1d6fea..ea5af8b505 100644 --- a/packages/app/src/identityProviders.ts +++ b/packages/app/src/identityProviders.ts @@ -30,14 +30,14 @@ export const providers = [ }, { id: 'gitlab-auth-provider', - title: 'Gitlab', - message: 'Sign In using Gitlab', + title: 'GitLab', + message: 'Sign In using GitLab', apiRef: gitlabAuthApiRef, }, { id: 'github-auth-provider', - title: 'Github', - message: 'Sign In using Github', + title: 'GitHub', + message: 'Sign In using GitHub', apiRef: githubAuthApiRef, }, { diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index a1a408fece..ffe44afb8a 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -222,7 +222,7 @@ export const googleAuthApiRef = createApiRef< }); /** - * Provides authentication towards Github APIs. + * Provides authentication towards GitHub APIs. * * See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ * for a full list of supported scopes. @@ -231,7 +231,7 @@ export const githubAuthApiRef = createApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi >({ id: 'core.auth.github', - description: 'Provides authentication towards Github APIs', + description: 'Provides authentication towards GitHub APIs', }); /** @@ -252,7 +252,7 @@ export const oktaAuthApiRef = createApiRef< }); /** - * Provides authentication towards Gitlab APIs. + * Provides authentication towards GitLab APIs. * * See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token * for a full list of supported scopes. @@ -261,7 +261,7 @@ export const gitlabAuthApiRef = createApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi >({ id: 'core.auth.gitlab', - description: 'Provides authentication towards Gitlab APIs', + description: 'Provides authentication towards GitLab APIs', }); /** diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx index e69d63186d..74fa5e173a 100644 --- a/packages/core/src/layout/Sidebar/UserSettings.tsx +++ b/packages/core/src/layout/Sidebar/UserSettings.tsx @@ -62,14 +62,14 @@ export function SidebarUserSettings() { )} {providers.includes('github') && ( )} {providers.includes('gitlab') && ( diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index e6ba56d2c3..b63acdfb20 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -74,7 +74,7 @@ export class GithubAuthProvider implements OAuthProviderHandlers { idToken: params.id_token, }; - // Github provides an id numeric value (123) + // GitHub provides an id numeric value (123) // as a fallback const id = passportProfile!.id; diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts index cd46d97d13..c43c68591e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts @@ -50,7 +50,7 @@ describe('GitlabApiReaderProcessor', () => { 'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/', url: null, err: - 'Incorrect url: https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/, Error: Gitlab url does not end in .ya?ml', + 'Incorrect url: https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/, Error: GitLab url does not end in .ya?ml', }, ]; diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts index 6964640eb4..3e585bf2b3 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts @@ -77,7 +77,7 @@ export class GitlabApiReaderProcessor implements LocationProcessor { const branchAndfilePath = url.pathname.split('/-/blob/')[1]; if (!branchAndfilePath.match(/\.ya?ml$/)) { - throw new Error('Gitlab url does not end in .ya?ml'); + throw new Error('GitLab url does not end in .ya?ml'); } const [branch, ...filePath] = branchAndfilePath.split('/'); @@ -127,7 +127,7 @@ export class GitlabApiReaderProcessor implements LocationProcessor { return projectID; } catch (e) { - throw new Error(`Could not get Gitlab ProjectID for: ${target}, ${e}`); + throw new Error(`Could not get GitLab ProjectID for: ${target}, ${e}`); } } } diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts index 211f325afe..9f308ee184 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts @@ -77,7 +77,7 @@ export class GitlabReaderProcessor implements LocationProcessor { blobKeyword !== 'blob' || !restOfPath.join('/').match(/\.yaml$/) ) { - throw new Error('Wrong Gitlab URL'); + throw new Error('Wrong GitLab URL'); } // Replace 'blob' with 'raw' diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index acdfb90cf3..9a95f21860 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -23,7 +23,7 @@ import { export const githubActionsApiRef = createApiRef({ id: 'plugin.githubactions.service', - description: 'Used by the Github Actions plugin to make requests', + description: 'Used by the GitHub Actions plugin to make requests', }); export type GithubActionsApi = { diff --git a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts index ee0b4bfd4e..23a9e9e697 100644 --- a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts +++ b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts @@ -33,7 +33,7 @@ export type GithubEndpointConfig = { id: string; title: string; /** - * Github GraphQL API url, defaults to https://api.github.com/graphql + * GitHub GraphQL API url, defaults to https://api.github.com/graphql */ url?: string; /** diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts index 164ec316df..93493ab2cc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts @@ -45,7 +45,7 @@ const { mockRemote: jest.Mocked; }; -describe('Github Publisher', () => { +describe('GitHub Publisher', () => { const publisher = new GithubPublisher({ client: new Octokit() }); beforeEach(() => { From f6f56aa7484b50f9c2293eddd91fc3d6ccaa73f7 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 21 Aug 2020 14:56:46 +0200 Subject: [PATCH 26/70] TechDocs: documentation template (#1954) * feat(docs-template): Add docs template to create a standalone docs project * fix(docs-template): add docs template to mock data * fix(docs-template): owner of template * add techdocs-ref to cookiecutter docs project * delete unused cookiecutter json file * update docs in default index.md file * rename from .yaml to .yml to be consistent to docs * update techdocs-ref to use github protocol --- .../docs-template/template.yaml | 29 +++++++++++++++++++ .../component-info.yaml | 12 ++++++++ .../docs/index.md | 28 ++++++++++++++++++ .../{{cookiecutter.component_id}}/mkdocs.yml | 8 +++++ .../scaffolder-backend/scripts/mock-data.sh | 1 + .../src/techdocs/stages/prepare/dir.ts | 3 +- .../src/techdocs/stages/prepare/github.ts | 3 +- 7 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/template.yaml create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml new file mode 100644 index 0000000000..01ebdadbce --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml @@ -0,0 +1,29 @@ +apiVersion: backstage.io/v1alpha1 +kind: Template +metadata: + name: docs-template + title: Documentation Template + description: Create a new standalone documentation project + tags: + - Experimental + - TechDocs + - MkDocs +spec: + owner: spotify/techdocs-core + templater: cookiecutter + type: documentation + path: '.' + + schema: + required: + - component_id + properties: + component_id: + title: Name + type: string + description: Unique name of the component + description: + title: Description + type: string + description: Description of the component + \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml new file mode 100644 index 0000000000..854b692fcf --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: {{cookiecutter.component_id}} + description: {{cookiecutter.description}} + annotations: + github.com/project-slug: {{cookiecutter.storePath}} + backstage.io/techdocs-ref: github:https://github.com/{{cookiecutter.storePath}} +spec: + type: documentation + lifecycle: experimental + owner: {{cookiecutter.owner}} diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md new file mode 100644 index 0000000000..5352ef7801 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md @@ -0,0 +1,28 @@ +## {{ cookiecutter.component_id }} + +{{ cookiecutter.description }} + +## Getting started + +Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. + +## Table of Contents + +The Table of Contents on the right is generated automatically based on the hierarchy +of headings. Only use one H1 (`#` in Markdown) per file. + +## Site navigation + +For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` +file in root of your repo. The navigation can also link out to other sites. + +Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section +will be created for you. However, you will not be able to use alternate titles for +pages, or include links to other sites. + +Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. +See also . + +## Support + +That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml new file mode 100644 index 0000000000..2126971502 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml @@ -0,0 +1,8 @@ +site_name: {{cookiecutter.component_id}} +site_description: {{cookiecutter.description}} + +nav: + - Introduction: index.md + +plugins: + - techdocs-core diff --git a/plugins/scaffolder-backend/scripts/mock-data.sh b/plugins/scaffolder-backend/scripts/mock-data.sh index f18d4b4838..9a9b0b186b 100755 --- a/plugins/scaffolder-backend/scripts/mock-data.sh +++ b/plugins/scaffolder-backend/scripts/mock-data.sh @@ -4,6 +4,7 @@ for URL in \ 'react-ssr-template' \ 'springboot-grpc-template' \ 'create-react-app' \ + 'docs-template' \ ; do \ curl \ --location \ diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts index 1560c2ea8e..e108ac4ab9 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts @@ -43,7 +43,8 @@ export class DirectoryPreparer implements PreparerBase { const parsedGitLocation = parseGitUrl(target); const repositoryTmpPath = path.join( - os.tmpdir(), + // fs.realpathSync fixes a problem with macOS returning a path that is a symlink + fs.realpathSync(os.tmpdir()), 'backstage-repo', parsedGitLocation.source, parsedGitLocation.owner, diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts index 27ee277f02..aa8a4bb228 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts @@ -43,7 +43,8 @@ export class GithubPreparer implements PreparerBase { const parsedGitLocation = parseGitUrl(target); const repositoryTmpPath = path.join( - os.tmpdir(), + // fs.realpathSync fixes a problem with macOS returning a path that is a symlink + fs.realpathSync(os.tmpdir()), 'backstage-repo', parsedGitLocation.source, parsedGitLocation.owner, From 2e50876160e54a2a622e56affec09c270f1dd133 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 15:30:50 +0200 Subject: [PATCH 27/70] Throw error when failing to generate docs to prevent request loop (#2073) --- .../techdocs-backend/src/techdocs/stages/generate/techdocs.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 96449a0bd6..40cd7590b6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -61,6 +61,9 @@ export class TechdocsGenerator implements GeneratorBase { this.logger.debug( `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`, ); + throw new Error( + `Failed to generate docs from ${directory} into ${resultDir} with error ${error.message}`, + ); } return { resultDir }; From e9c937dd9f7c2ac4957ad0c31822b02d35f8a257 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 21 Aug 2020 15:46:10 +0200 Subject: [PATCH 28/70] Add docs template to default app templates (#2072) --- packages/create-app/templates/default-app/app-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/create-app/templates/default-app/app-config.yaml b/packages/create-app/templates/default-app/app-config.yaml index 49c3dc3691..0c3ec3ed80 100644 --- a/packages/create-app/templates/default-app/app-config.yaml +++ b/packages/create-app/templates/default-app/app-config.yaml @@ -54,3 +54,5 @@ catalog: target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml - type: github target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml + - type: github + target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml From 0afdba6ac175205d1040348bd0a4a5d930471483 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 15:47:45 +0200 Subject: [PATCH 29/70] chore(deps): bump @rjsf/core from 2.2.2 to 2.3.0 (#2062) Bumps [@rjsf/core](https://github.com/rjsf-team/react-jsonschema-form) from 2.2.2 to 2.3.0. - [Release notes](https://github.com/rjsf-team/react-jsonschema-form/releases) - [Commits](https://github.com/rjsf-team/react-jsonschema-form/compare/v2.2.2...v2.3.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 06324a67a3..5a42953413 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3279,9 +3279,9 @@ react-lifecycles-compat "^3.0.4" "@rjsf/core@^2.1.0": - version "2.2.2" - resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.2.2.tgz#1ebb6fe47448998f3b54e2dea8d58de8a46014dc" - integrity sha512-4d6DHIiTJEkUq5vyl4LIxLGIYYKKnHcprf94oVchUtGQvRFjNUDFxeFQoyr90oaxcBMs2WDDcCgjcFaKVyfErg== + version "2.3.0" + resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.3.0.tgz#334c73d2262ef1a8cda477e238067af7336c5599" + integrity sha512-OZKYHt9tjKhzOH4CvsPiCwepuIacqI++cNmnL2fsxh1IF+uEWGlo3NLDWhhSaBbOv9jps6a5YQcLbLtjNuSwug== dependencies: "@babel/runtime-corejs2" "^7.8.7" "@types/json-schema" "^7.0.4" @@ -5845,17 +5845,7 @@ ajv@^5.0.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@^6.7.0: - version "6.12.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.10.1: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@^6.7.0: version "6.12.3" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== From 3e10e5e3d8665edd3529615b1e4af4949dcb1f8f Mon Sep 17 00:00:00 2001 From: Nikita Dudnik Date: Fri, 21 Aug 2020 15:48:08 +0200 Subject: [PATCH 30/70] fix(microsite): animation for techdocs (#2070) --- microsite/pages/en/index.js | 18 ++++++++++++++---- microsite/static/img/techdocs2.gif | Bin 0 -> 89705 bytes 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 microsite/static/img/techdocs2.gif diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index d140a2036b..986db4b732 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -353,10 +353,20 @@ class Index extends React.Component { + + + + } /> diff --git a/microsite/static/img/techdocs2.gif b/microsite/static/img/techdocs2.gif new file mode 100644 index 0000000000000000000000000000000000000000..ba937581b3f71e6516d401b25f4260223a570a17 GIT binary patch literal 89705 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 m5vSPW?qKP{i8$?5eY^t&TNc Date: Fri, 21 Aug 2020 15:38:12 -0500 Subject: [PATCH 31/70] show how to use config when setting up api (#2075) Most API will require some configuration. So it would be more useful to show an example where the config is available when the API's are being wired up --- docs/api/utility-apis.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index ff8c91e178..b4c2fd161e 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -71,18 +71,23 @@ import { AlertApiForwarder, ErrorApiForwarder, ErrorAlerter, + ConfigApi } from '@backstage/core'; -const builder = ApiRegistry.builder(); -// The alert API is a self-contained implementation that shows alerts to the user. -const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); +const apis = (config: ConfigApi) => { + const builder = ApiRegistry.builder(); -// The error API uses the alert API to send error notifications to the user. -builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder())); + // The alert API is a self-contained implementation that shows alerts to the user. + const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); + + // The error API uses the alert API to send error notifications to the user. + builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder())); + return builder.build(); +} const app = createApp({ - apis: apiBuilder.build(), + apis, // ... other config }); ``` From d224f9bba7c96a652e767043af3beea79fc8181d Mon Sep 17 00:00:00 2001 From: Mustansar Anwar ul Samad Date: Sat, 22 Aug 2020 10:04:17 +1200 Subject: [PATCH 32/70] Fix webpack sourcemap path for debugging --- packages/cli/src/lib/bundler/config.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 8049ce436d..be81b4bb59 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -198,6 +198,11 @@ export function createBackendConfig( chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js', + ...(isDev + ? { + devtoolModuleFilenameTemplate: 'file:///[absolute-resource-path]', + } + : {}), }, plugins: [ new StartServerPlugin('main.js'), From 290d28d856095ae8b39f0111b40d4ef70cc6c3fc Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Sat, 22 Aug 2020 10:05:21 +0200 Subject: [PATCH 33/70] Update microsite i18n json --- microsite/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index d2593a13ce..698d86a26a 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -199,7 +199,7 @@ "title": "Backend plugin" }, "plugins/call-existing-api": { - "title": "Call existing API" + "title": "Call Existing API" }, "plugins/create-a-plugin": { "title": "Create a Backstage Plugin" From 950b316820c04518d30cf21ba10058052891df82 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 22 Aug 2020 10:08:06 +0200 Subject: [PATCH 34/70] cli: switch ts-jest to use isolatedModules --- packages/cli/config/jest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index d95dd21489..cc7d45c139 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -37,7 +37,10 @@ async function getConfig() { // TODO: jest is working on module support, it's possible that we can remove this in the future transform: { '\\.esm\\.js$': require.resolve('jest-esm-transformer'), - '\\.(js|jsx|ts|tsx)$': require.resolve('ts-jest'), + '\\.(js|jsx|ts|tsx)$': [ + require.resolve('ts-jest'), + { isolatedModules: true }, + ], '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve( './jestFileTransform.js', ), From d7a10bcf127788d797e2ee2a469d2a76100434f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sun, 23 Aug 2020 09:39:53 +0200 Subject: [PATCH 35/70] Update README.md (#2083) --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index ee9e85fd7f..b5e8704548 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,6 @@ For more information go to [backstage.io](https://backstage.io) or join our [Dis A detailed project roadmap, including already delivered milestones, is available [here](https://backstage.io/docs/overview/roadmap). -## Overview - -The Backstage platform consists of a number of different components: - -- **app** - Main web application that users interact with. It's built up by a number of different _Plugins_. This repo contains an example implementation of an app (located in `packages/app`) and you can easily get started with your own app by [creating one](docs/getting-started/create-an-app.md). -- [**plugins**](https://github.com/spotify/backstage/tree/master/plugins) - Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform API's and reusable UI components. Plugins can fetch data either from the _backend_ or through any RESTful API exposed through the _proxy_. -- [**service catalog**](https://github.com/spotify/backstage/tree/master/packages/backend) - Service that holds the model of your software ecosystem, including organisational information and what team owns what software. The backend also has a Plugin model for extending its graph. -- [**proxy**](https://github.com/spotify/backstage/tree/master/plugins/proxy-backend) - Terminates HTTPS and exposes any RESTful API to Plugins. -- **identity** - A backend service that holds your organisation's metadata. - ## Getting Started There are two different ways to get started with Backstage, either by creating a standalone app, or by cloning this repo. Which method you use depends on what you're planning to do. From 5f1c74452acbb294e32e21f8e2889343253155e0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 16:18:48 +0200 Subject: [PATCH 36/70] workflows: rename Frontend CI to just CI --- .github/workflows/{frontend.yml => ci.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{frontend.yml => ci.yml} (98%) diff --git a/.github/workflows/frontend.yml b/.github/workflows/ci.yml similarity index 98% rename from .github/workflows/frontend.yml rename to .github/workflows/ci.yml index dc65b987a8..105642e284 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,11 @@ -name: Frontend CI +name: CI on: pull_request: paths-ignore: - 'microsite/**' jobs: - build: + verify: runs-on: ubuntu-latest strategy: From 1358739063eae124dce5ba11064b891a1ade68ed Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 11:25:10 +0200 Subject: [PATCH 37/70] cli: move out e2e-test to separate package --- packages/cli/package.json | 7 +--- packages/e2e-test/.eslintrc.js | 7 ++++ packages/e2e-test/README.md | 24 +++++++++++++ packages/e2e-test/package.json | 35 +++++++++++++++++++ .../e2e-test => e2e-test/src}/.eslintrc.js | 0 .../{cli/e2e-test => e2e-test/src}/helpers.js | 0 .../cli-e2e-test.js => e2e-test/src/index.js} | 0 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 packages/e2e-test/.eslintrc.js create mode 100644 packages/e2e-test/README.md create mode 100644 packages/e2e-test/package.json rename packages/{cli/e2e-test => e2e-test/src}/.eslintrc.js (100%) rename packages/{cli/e2e-test => e2e-test/src}/helpers.js (100%) rename packages/{cli/e2e-test/cli-e2e-test.js => e2e-test/src/index.js} (100%) diff --git a/packages/cli/package.json b/packages/cli/package.json index e5a8eafc81..0291e720d2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,7 +21,6 @@ "build": "backstage-cli build --outputs cjs", "lint": "backstage-cli lint", "test": "backstage-cli test", - "test:e2e": "node e2e-test/cli-e2e-test.js", "clean": "backstage-cli clean", "start": "nodemon --" }, @@ -69,9 +68,7 @@ "jest-css-modules": "^2.1.0", "jest-esm-transformer": "^1.0.0", "mini-css-extract-plugin": "^0.9.0", - "node-fetch": "^2.6.0", "ora": "^4.0.3", - "pgtools": "^0.3.0", "raw-loader": "^4.0.1", "react": "^16.0.0", "react-dev-utils": "^10.2.1", @@ -118,9 +115,7 @@ "@types/webpack-dev-server": "^3.10.0", "del": "^5.1.0", "nodemon": "^2.0.2", - "tree-kill": "^1.2.2", - "ts-node": "^8.6.2", - "zombie": "^6.1.4" + "ts-node": "^8.6.2" }, "files": [ "asset-types", diff --git a/packages/e2e-test/.eslintrc.js b/packages/e2e-test/.eslintrc.js new file mode 100644 index 0000000000..69bec6cd2a --- /dev/null +++ b/packages/e2e-test/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], + ignorePatterns: ['templates/**'], + rules: { + 'no-console': 0, + }, +}; diff --git a/packages/e2e-test/README.md b/packages/e2e-test/README.md new file mode 100644 index 0000000000..6cf50283c5 --- /dev/null +++ b/packages/e2e-test/README.md @@ -0,0 +1,24 @@ +# e2e-test + +End-to-end test for verifying Backstage packages. + +## Usage + +This package is only meant for usage within the Backstage monorepo. + +All packages need to be installed and built before running the test. In a fresh clone of this repo you first need to run the following from the repo root: + +```sh +yarn install +yarn tsc +yarn build +``` + +Once those tasks have completed, you can now run the test using `yarn start` inside this package. + +If you make changes to other packages you will need to rerun `yarn tsc && yarn build`. Changes to this package do not require a rebuild. + +## Documentation + +- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json new file mode 100644 index 0000000000..d2f9dc9784 --- /dev/null +++ b/packages/e2e-test/package.json @@ -0,0 +1,35 @@ +{ + "name": "e2e-test", + "description": "E2E test for verifying Backstage packages", + "version": "0.0.0", + "private": true, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/spotify/backstage", + "directory": "packages/e2e-test" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "main": "src/index.js", + "scripts": { + "start": "ts-node .", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "test:e2e": "yarn start" + }, + "devDependencies": { + "@backstage/cli-common": "^0.1.1-alpha.19", + "@types/fs-extra": "^9.0.1", + "@types/node": "^13.7.2", + "fs-extra": "^9.0.0", + "handlebars": "^4.7.3", + "node-fetch": "^2.6.0", + "pgtools": "^0.3.0", + "tree-kill": "^1.2.2", + "ts-node": "^8.6.2", + "zombie": "^6.1.4" + } +} diff --git a/packages/cli/e2e-test/.eslintrc.js b/packages/e2e-test/src/.eslintrc.js similarity index 100% rename from packages/cli/e2e-test/.eslintrc.js rename to packages/e2e-test/src/.eslintrc.js diff --git a/packages/cli/e2e-test/helpers.js b/packages/e2e-test/src/helpers.js similarity index 100% rename from packages/cli/e2e-test/helpers.js rename to packages/e2e-test/src/helpers.js diff --git a/packages/cli/e2e-test/cli-e2e-test.js b/packages/e2e-test/src/index.js similarity index 100% rename from packages/cli/e2e-test/cli-e2e-test.js rename to packages/e2e-test/src/index.js From 9052dfc5bf7d4fba51ca17fd271a2b3885c6e14a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 11:28:59 +0200 Subject: [PATCH 38/70] e2e-test: merge eslint rules --- packages/e2e-test/.eslintrc.js | 9 +++++++++ packages/e2e-test/src/.eslintrc.js | 29 ----------------------------- 2 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 packages/e2e-test/src/.eslintrc.js diff --git a/packages/e2e-test/.eslintrc.js b/packages/e2e-test/.eslintrc.js index 69bec6cd2a..ce2592e03b 100644 --- a/packages/e2e-test/.eslintrc.js +++ b/packages/e2e-test/.eslintrc.js @@ -3,5 +3,14 @@ module.exports = { ignorePatterns: ['templates/**'], rules: { 'no-console': 0, + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + optionalDependencies: false, + peerDependencies: false, + bundledDependencies: false, + }, + ], }, }; diff --git a/packages/e2e-test/src/.eslintrc.js b/packages/e2e-test/src/.eslintrc.js deleted file mode 100644 index 274c7426b8..0000000000 --- a/packages/e2e-test/src/.eslintrc.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module.exports = { - rules: { - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: true, - peerDependencies: true, - bundledDependencies: true, - }, - ], - }, -}; From 0d240696ebad85b2e2e78b346d08d9e283dab565 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 12:00:37 +0200 Subject: [PATCH 39/70] e2e-test: port to typscript --- packages/e2e-test/package.json | 4 +- .../e2e-test/src/{helpers.js => helpers.ts} | 48 ++++++------- packages/e2e-test/src/{index.js => index.ts} | 68 +++++++++++-------- packages/e2e-test/src/types.d.ts | 18 +++++ 4 files changed, 82 insertions(+), 56 deletions(-) rename packages/e2e-test/src/{helpers.js => helpers.ts} (84%) rename packages/e2e-test/src/{index.js => index.ts} (86%) create mode 100644 packages/e2e-test/src/types.d.ts diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index d2f9dc9784..389f4b6f93 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -13,9 +13,9 @@ "backstage" ], "license": "Apache-2.0", - "main": "src/index.js", + "main": "src/index.ts", "scripts": { - "start": "ts-node .", + "start": "yarn ts-node --transpile-only --compiler-options '{\"module\":\"CommonJS\"}' .", "lint": "backstage-cli lint", "test": "backstage-cli test", "test:e2e": "yarn start" diff --git a/packages/e2e-test/src/helpers.js b/packages/e2e-test/src/helpers.ts similarity index 84% rename from packages/e2e-test/src/helpers.js rename to packages/e2e-test/src/helpers.ts index 580f53cbe8..783b7e3988 100644 --- a/packages/e2e-test/src/helpers.js +++ b/packages/e2e-test/src/helpers.ts @@ -14,16 +14,21 @@ * limitations under the License. */ -const { spawn, execFile: execFileCb } = require('child_process'); -const { promisify } = require('util'); +import { + spawn, + execFile as execFileCb, + SpawnOptions, + ChildProcess, +} from 'child_process'; +import { promisify } from 'util'; const execFile = promisify(execFileCb); const EXPECTED_LOAD_ERRORS = /ECONNREFUSED|ECONNRESET|did not get to load all resources/; -function spawnPiped(cmd, options) { - function pipeWithPrefix(stream, prefix = '') { - return data => { +export function spawnPiped(cmd: string[], options?: SpawnOptions) { + function pipeWithPrefix(stream: NodeJS.WriteStream, prefix = '') { + return (data: Buffer) => { const prefixedMsg = data .toString('utf8') .trimRight() @@ -40,11 +45,11 @@ function spawnPiped(cmd, options) { child.on('error', handleError); const logPrefix = cmd.map(s => s.replace(/.+\//, '')).join(' '); - child.stdout.on( + child.stdout?.on( 'data', pipeWithPrefix(process.stdout, `[${logPrefix}].out: `), ); - child.stderr.on( + child.stderr?.on( 'data', pipeWithPrefix(process.stderr, `[${logPrefix}].err: `), ); @@ -52,7 +57,7 @@ function spawnPiped(cmd, options) { return child; } -async function runPlain(cmd, options) { +export async function runPlain(cmd: string[], options?: SpawnOptions) { try { const { stdout } = await execFile(cmd[0], cmd.slice(1), { ...options, @@ -70,8 +75,9 @@ async function runPlain(cmd, options) { } } -function handleError(err) { +export function handleError(err: Error & { code?: unknown }) { process.stdout.write(`${err.name}: ${err.stack || err.message}\n`); + if (typeof err.code === 'number') { process.exit(err.code); } else { @@ -85,7 +91,7 @@ function handleError(err) { * .cancel() is available * @returns {Promise} Promise of resolution */ -function waitFor(fn) { +export function waitFor(fn: () => boolean) { return new Promise(resolve => { const handle = setInterval(() => { if (fn()) { @@ -97,7 +103,7 @@ function waitFor(fn) { }); } -async function waitForExit(child) { +export async function waitForExit(child: ChildProcess) { if (child.exitCode !== null) { throw new Error(`Child already exited with code ${child.exitCode}`); } @@ -113,10 +119,10 @@ async function waitForExit(child) { ); } -async function waitForPageWithText( - browser, - path, - text, +export async function waitForPageWithText( + browser: any, + path: string, + text: string, { intervalMs = 1000, maxLoadAttempts = 240, maxFindTextAttempts = 3 } = {}, ) { let loadAttempts = 0; @@ -163,16 +169,6 @@ async function waitForPageWithText( } } -function print(msg) { +export function print(msg: string) { return process.stdout.write(`${msg}\n`); } - -module.exports = { - spawnPiped, - runPlain, - handleError, - waitFor, - waitForExit, - waitForPageWithText, - print, -}; diff --git a/packages/e2e-test/src/index.js b/packages/e2e-test/src/index.ts similarity index 86% rename from packages/e2e-test/src/index.js rename to packages/e2e-test/src/index.ts index c4004f7c46..a455964f44 100644 --- a/packages/e2e-test/src/index.js +++ b/packages/e2e-test/src/index.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -const os = require('os'); -const fs = require('fs-extra'); -const fetch = require('node-fetch'); -const handlebars = require('handlebars'); -const killTree = require('tree-kill'); -const { resolve: resolvePath, join: joinPath } = require('path'); -const Browser = require('zombie'); -const { +import os from 'os'; +import fs from 'fs-extra'; +import fetch from 'node-fetch'; +import handlebars from 'handlebars'; +import killTree from 'tree-kill'; +import { resolve as resolvePath, join as joinPath } from 'path'; +import Browser from 'zombie'; +import { spawnPiped, runPlain, handleError, @@ -29,8 +29,8 @@ const { waitFor, waitForExit, print, -} = require('./helpers'); -const pgtools = require('pgtools'); +} from './helpers'; +import pgtools from 'pgtools'; async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); @@ -59,7 +59,7 @@ async function main() { /** * Builds a dist workspace that contains the cli and core packages */ -async function buildDistWorkspace(workspaceName, rootDir) { +async function buildDistWorkspace(workspaceName: string, rootDir: string) { const workspaceDir = resolvePath(rootDir, workspaceName); await fs.ensureDir(workspaceDir); @@ -121,13 +121,20 @@ async function buildDistWorkspace(workspaceName, rootDir) { /** * Pin the yarn version in a directory to the one we're using in the Backstage repo */ -async function pinYarnVersion(dir) { +async function pinYarnVersion(dir: string) { const repoRoot = resolvePath(__dirname, '../../..'); const yarnRc = await fs.readFile(resolvePath(repoRoot, '.yarnrc'), 'utf8'); const yarnRcLines = yarnRc.split('\n'); const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarn-path')); - const [, localYarnPath] = yarnPathLine.match(/"(.*)"/); + if (!yarnPathLine) { + throw new Error(`Unable to find 'yarn-path' in ${yarnRc}`); + } + const match = yarnPathLine.match(/"(.*)"/); + if (!match) { + throw new Error(`Invalid 'yarn-path' in ${yarnRc}`); + } + const [, localYarnPath] = match; const yarnPath = resolvePath(repoRoot, localYarnPath); await fs.writeFile(resolvePath(dir, '.yarnrc'), `yarn-path "${yarnPath}"\n`); @@ -136,7 +143,12 @@ async function pinYarnVersion(dir) { /** * Creates a new app inside rootDir called test-app, using packages from the workspaceDir */ -async function createApp(appName, isPostgres, workspaceDir, rootDir) { +async function createApp( + appName: string, + isPostgres: boolean, + workspaceDir: string, + rootDir: string, +) { const child = spawnPiped( [ 'node', @@ -150,20 +162,20 @@ async function createApp(appName, isPostgres, workspaceDir, rootDir) { try { let stdout = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', data => { stdout = stdout + data.toString('utf8'); }); await waitFor(() => stdout.includes('Enter a name for the app')); - child.stdin.write(`${appName}\n`); + child.stdin?.write(`${appName}\n`); await waitFor(() => stdout.includes('Select database for the backend')); if (!isPostgres) { // Simulate down arrow press - child.stdin.write(`\u001B\u005B\u0042`); + child.stdin?.write(`\u001B\u005B\u0042`); } - child.stdin.write(`\n`); + child.stdin?.write(`\n`); print('Waiting for app create script to be done'); await waitForExit(child); @@ -205,7 +217,7 @@ async function createApp(appName, isPostgres, workspaceDir, rootDir) { /** * This points dependency resolutions into the workspace for each package that is present there */ -async function overrideModuleResolutions(appDir, workspaceDir) { +async function overrideModuleResolutions(appDir: string, workspaceDir: string) { const pkgJsonPath = resolvePath(appDir, 'package.json'); const pkgJson = await fs.readJson(pkgJsonPath); @@ -231,19 +243,19 @@ async function overrideModuleResolutions(appDir, workspaceDir) { /** * Uses create-plugin command to create a new plugin in the app */ -async function createPlugin(pluginName, appDir) { +async function createPlugin(pluginName: string, appDir: string) { const child = spawnPiped(['yarn', 'create-plugin'], { cwd: appDir, }); try { let stdout = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', (data: Buffer) => { stdout = stdout + data.toString('utf8'); }); await waitFor(() => stdout.includes('Enter an ID for the plugin')); - child.stdin.write(`${pluginName}\n`); + child.stdin?.write(`${pluginName}\n`); // await waitFor(() => stdout.includes('Enter the owner(s) of the plugin')); // child.stdin.write('@someuser\n'); @@ -266,7 +278,7 @@ async function createPlugin(pluginName, appDir) { /** * Start serving the newly created app and make sure that the create plugin is rendering correctly */ -async function testAppServe(pluginName, appDir) { +async function testAppServe(pluginName: string, appDir: string) { const startApp = spawnPiped(['yarn', 'start'], { cwd: appDir, }); @@ -302,7 +314,7 @@ async function testAppServe(pluginName, appDir) { } /** Creates PG databases (drops if exists before) */ -async function createDB(database) { +async function createDB(database: string) { const config = { host: process.env.POSTGRES_HOST, port: process.env.POSTGRES_PORT, @@ -321,7 +333,7 @@ async function createDB(database) { /** * Start serving the newly created backend and make sure that all db migrations works correctly */ -async function testBackendStart(appDir, isPostgres) { +async function testBackendStart(appDir: string, isPostgres: boolean) { if (isPostgres) { print('Creating DBs'); await Promise.all( @@ -343,10 +355,10 @@ async function testBackendStart(appDir, isPostgres) { let stdout = ''; let stderr = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', (data: Buffer) => { stdout = stdout + data.toString('utf8'); }); - child.stderr.on('data', data => { + child.stderr?.on('data', (data: Buffer) => { stderr = stderr + data.toString('utf8'); }); let successful = false; @@ -384,4 +396,4 @@ async function testBackendStart(appDir, isPostgres) { } process.on('unhandledRejection', handleError); -main(process.argv.slice(2)).catch(handleError); +main().catch(handleError); diff --git a/packages/e2e-test/src/types.d.ts b/packages/e2e-test/src/types.d.ts new file mode 100644 index 0000000000..0ae86490ee --- /dev/null +++ b/packages/e2e-test/src/types.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare module 'zombie'; +declare module 'pgtools'; From 831ce12b7d3b31325688fc001f4737aa3da078b9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 12:21:17 +0200 Subject: [PATCH 40/70] e2e-test: refactor dep detection and use cli-common to resolve paths --- packages/e2e-test/src/index.ts | 60 +++++++++++++--------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/packages/e2e-test/src/index.ts b/packages/e2e-test/src/index.ts index a455964f44..46992ab966 100644 --- a/packages/e2e-test/src/index.ts +++ b/packages/e2e-test/src/index.ts @@ -31,6 +31,9 @@ import { print, } from './helpers'; import pgtools from 'pgtools'; +import { findPaths } from '@backstage/cli-common'; + +const paths = findPaths(__dirname); async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); @@ -63,34 +66,22 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { const workspaceDir = resolvePath(rootDir, workspaceName); await fs.ensureDir(workspaceDir); - // We grab the needed dependencies from the template packages - const appPkgTemplate = await fs.readFile( - resolvePath( - __dirname, - '../../create-app/templates/default-app/packages/app/package.json.hbs', - ), - 'utf8', - ); - const appPkg = JSON.parse( - handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), - ); - const appDeps = Object.keys(appPkg.dependencies).filter(name => - name.startsWith('@backstage/'), - ); - - const backendPkgTemplate = await fs.readFile( - resolvePath( - __dirname, - '../../create-app/templates/default-app/packages/backend/package.json.hbs', - ), - 'utf8', - ); - const backendPkg = JSON.parse( - handlebars.compile(backendPkgTemplate)({ version: '0.0.0' }), - ); - const backendDeps = Object.keys(backendPkg.dependencies).filter(name => - name.startsWith('@backstage/'), - ); + // We grab the needed dependencies from the create app template + const createAppDeps: string[] = []; + for (const pkgPath of ['packages/app', 'packages/backend']) { + const path = paths.resolveOwnRoot( + 'packages/create-app/templates/default-app', + pkgPath, + 'package.json.hbs', + ); + const appPkgTemplate = await fs.readFile(path, 'utf8'); + const { dependencies: allDeps } = JSON.parse( + handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), + ); + createAppDeps.push( + ...Object.keys(allDeps).filter(name => name.startsWith('@backstage/')), + ); + } print(`Preparing workspace`); await runPlain([ @@ -98,13 +89,8 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { 'backstage-cli', 'build-workspace', workspaceDir, - '@backstage/cli', '@backstage/create-app', - '@backstage/core', - '@backstage/dev-utils', - '@backstage/test-utils', - ...appDeps, - ...backendDeps, + ...createAppDeps, ]); print('Pinning yarn version in workspace'); @@ -122,9 +108,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { * Pin the yarn version in a directory to the one we're using in the Backstage repo */ async function pinYarnVersion(dir: string) { - const repoRoot = resolvePath(__dirname, '../../..'); - - const yarnRc = await fs.readFile(resolvePath(repoRoot, '.yarnrc'), 'utf8'); + const yarnRc = await fs.readFile(paths.resolveOwnRoot('.yarnrc'), 'utf8'); const yarnRcLines = yarnRc.split('\n'); const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarn-path')); if (!yarnPathLine) { @@ -135,7 +119,7 @@ async function pinYarnVersion(dir: string) { throw new Error(`Invalid 'yarn-path' in ${yarnRc}`); } const [, localYarnPath] = match; - const yarnPath = resolvePath(repoRoot, localYarnPath); + const yarnPath = paths.resolveOwnRoot(localYarnPath); await fs.writeFile(resolvePath(dir, '.yarnrc'), `yarn-path "${yarnPath}"\n`); } From 0d09f12c4d51a0cbab012c4c86f0699c71993a2f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 13:28:16 +0200 Subject: [PATCH 41/70] workflows: update e2e test workflows --- .github/workflows/{cli-win.yml => e2e-win.yml} | 14 ++++++++------ .github/workflows/{cli.yml => e2e.yml} | 14 +++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) rename .github/workflows/{cli-win.yml => e2e-win.yml} (77%) rename .github/workflows/{cli.yml => e2e.yml} (87%) diff --git a/.github/workflows/cli-win.yml b/.github/workflows/e2e-win.yml similarity index 77% rename from .github/workflows/cli-win.yml rename to .github/workflows/e2e-win.yml index 4e7a186282..5f75f3d9c8 100644 --- a/.github/workflows/cli-win.yml +++ b/.github/workflows/e2e-win.yml @@ -1,12 +1,13 @@ -name: CLI Test Windows +name: E2E Test Windows -# Building on windows is really slow, so this workflow is separate from cli.yml and only builds on changes +# Building on windows is really slow, so this workflow is separate from e2e.yml and only builds on changes # to the cli itself. They're more likely to introduce issues on windows, compared to changes to core and yarn.lock. on: pull_request: paths: - - '.github/workflows/cli-win.yml' + - '.github/workflows/e2e-win.yml' - 'packages/cli/**' + - 'packages/e2e/**' - 'packages/create-app/**' jobs: @@ -42,6 +43,7 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile - run: yarn tsc - - run: yarn build - - name: verify app and plugin creation - run: node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js + - name: yarn build + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + - name: run E2E test + run: yarn workspace e2e-test start diff --git a/.github/workflows/cli.yml b/.github/workflows/e2e.yml similarity index 87% rename from .github/workflows/cli.yml rename to .github/workflows/e2e.yml index 158310b5ea..45b9d7e2d8 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/e2e.yml @@ -1,11 +1,10 @@ -name: CLI Test +name: E2E Test Linux on: pull_request: paths-ignore: - 'microsite/**' - jobs: build: runs-on: ${{ matrix.os }} @@ -51,13 +50,14 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile - run: yarn tsc - - run: yarn build - - name: verify app and plugin creation + - name: yarn build + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + - name: run E2E test + run: | + sudo sysctl fs.inotify.max_user_watches=524288 + yarn workspace e2e-test start env: POSTGRES_HOST: localhost POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - run: | - sudo sysctl fs.inotify.max_user_watches=524288 - node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js From 5fc0ce3e375b62704dd92d2bd66abdd447eac75b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 13:43:18 +0200 Subject: [PATCH 42/70] e2e-test: make waitFor time out --- packages/e2e-test/src/helpers.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/e2e-test/src/helpers.ts b/packages/e2e-test/src/helpers.ts index 783b7e3988..b59de75860 100644 --- a/packages/e2e-test/src/helpers.ts +++ b/packages/e2e-test/src/helpers.ts @@ -91,9 +91,14 @@ export function handleError(err: Error & { code?: unknown }) { * .cancel() is available * @returns {Promise} Promise of resolution */ -export function waitFor(fn: () => boolean) { - return new Promise(resolve => { +export function waitFor(fn: () => boolean, maxSeconds: number = 120) { + let count = 0; + return new Promise((resolve, reject) => { const handle = setInterval(() => { + if (count++ > maxSeconds * 10) { + reject(new Error('Timed out while waiting for condition')); + return; + } if (fn()) { clearInterval(handle); resolve(); From b73612df6476122043a9da08a29b2eba7b7d73fb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 14:56:05 +0200 Subject: [PATCH 43/70] e2e-test: add minimal test --- packages/e2e-test/src/helpers.test.ts | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/e2e-test/src/helpers.test.ts diff --git a/packages/e2e-test/src/helpers.test.ts b/packages/e2e-test/src/helpers.test.ts new file mode 100644 index 0000000000..196241f9e0 --- /dev/null +++ b/packages/e2e-test/src/helpers.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { waitFor } from './helpers'; + +describe('waitFor', () => { + it('should wait for true', async () => { + const fn = jest.fn().mockReturnValue(true); + await waitFor(fn); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('should time out', async () => { + const fn = jest.fn().mockReturnValue(false); + await expect(waitFor(fn, 1)).rejects.toThrow( + 'Timed out while waiting for condition', + ); + expect(fn).toHaveBeenCalled(); + }); +}); From e8e6deb0e46c48585a702837379900a133d7131f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:33:15 +0200 Subject: [PATCH 44/70] e2e-test: switch to using ts-node register to pass options --- packages/e2e-test/package.json | 4 +-- .../e2e-test/src/{index.ts => e2e-test.ts} | 0 packages/e2e-test/src/index.js | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) rename packages/e2e-test/src/{index.ts => e2e-test.ts} (100%) create mode 100644 packages/e2e-test/src/index.js diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 389f4b6f93..8e6c730431 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -13,9 +13,9 @@ "backstage" ], "license": "Apache-2.0", - "main": "src/index.ts", + "main": "src/index.js", "scripts": { - "start": "yarn ts-node --transpile-only --compiler-options '{\"module\":\"CommonJS\"}' .", + "start": "node .", "lint": "backstage-cli lint", "test": "backstage-cli test", "test:e2e": "yarn start" diff --git a/packages/e2e-test/src/index.ts b/packages/e2e-test/src/e2e-test.ts similarity index 100% rename from packages/e2e-test/src/index.ts rename to packages/e2e-test/src/e2e-test.ts diff --git a/packages/e2e-test/src/index.js b/packages/e2e-test/src/index.js new file mode 100644 index 0000000000..90f8030a0e --- /dev/null +++ b/packages/e2e-test/src/index.js @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require('ts-node').register({ + transpileOnly: true, + project: require('path').resolve(__dirname, '../../../tsconfig.json'), + compilerOptions: { + module: 'CommonJS', + }, +}); + +require('./e2e-test'); From 5b722c179fb43932cf6109a389dc8d6a57e209dd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:48:34 +0200 Subject: [PATCH 45/70] e2e-test: include local deps from plugin template --- packages/e2e-test/src/e2e-test.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 46992ab966..7377655497 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -35,6 +35,12 @@ import { findPaths } from '@backstage/cli-common'; const paths = findPaths(__dirname); +const templatePackagePaths = [ + 'packages/cli/templates/default-plugin/package.json.hbs', + 'packages/create-app/templates/default-app/packages/app/package.json.hbs', + 'packages/create-app/templates/default-app/packages/backend/package.json.hbs', +]; + async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); print(`CLI E2E test root: ${rootDir}\n`); @@ -67,20 +73,18 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { await fs.ensureDir(workspaceDir); // We grab the needed dependencies from the create app template - const createAppDeps: string[] = []; - for (const pkgPath of ['packages/app', 'packages/backend']) { - const path = paths.resolveOwnRoot( - 'packages/create-app/templates/default-app', - pkgPath, - 'package.json.hbs', - ); - const appPkgTemplate = await fs.readFile(path, 'utf8'); - const { dependencies: allDeps } = JSON.parse( - handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), - ); - createAppDeps.push( - ...Object.keys(allDeps).filter(name => name.startsWith('@backstage/')), + const createAppDeps = new Set(); + for (const pkgJsonPath of templatePackagePaths) { + const path = paths.resolveOwnRoot(pkgJsonPath); + const pkgTemplate = await fs.readFile(path, 'utf8'); + const { dependencies = {}, devDependencies = {} } = JSON.parse( + handlebars.compile(pkgTemplate)({ version: '0.0.0' }), ); + + Array() + .concat(Object.keys(dependencies), Object.keys(devDependencies)) + .filter(name => name.startsWith('@backstage/')) + .forEach(dep => createAppDeps.add(dep)); } print(`Preparing workspace`); From 4e5422bd34275f82a479dbc48c5ba8e46b3b62d0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:55:29 +0200 Subject: [PATCH 46/70] workflows: add Windows master build --- .github/workflows/master-win.yml | 47 ++++++++++++++++++++++++++++++++ .github/workflows/master.yml | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/master-win.yml diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml new file mode 100644 index 0000000000..074f944250 --- /dev/null +++ b/.github/workflows/master-win.yml @@ -0,0 +1,47 @@ +name: Master Build Windows + +on: + push: + branches: [master] + +jobs: + build: + runs-on: windows-latest + + strategy: + matrix: + node-version: [12.x] + + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 + + steps: + - uses: actions/checkout@v2 + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: yarn install + run: yarn install --frozen-lockfile + +# Tests are broken on Windows, disabled for now + # - name: test + # run: yarn lerna -- run test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 3ef0c1fee3..805095ec53 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -1,4 +1,4 @@ -name: Master Build +name: Main Master Build on: push: From ed733cf02967d29a549c77d887f1276a83868bc6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 18:01:13 +0200 Subject: [PATCH 47/70] docsgen: fix link generation on windows --- packages/docgen/src/docgen/ApiDocGenerator.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/docgen/src/docgen/ApiDocGenerator.ts b/packages/docgen/src/docgen/ApiDocGenerator.ts index bfe449155b..3edcfedb9c 100644 --- a/packages/docgen/src/docgen/ApiDocGenerator.ts +++ b/packages/docgen/src/docgen/ApiDocGenerator.ts @@ -15,7 +15,11 @@ */ import ts from 'typescript'; -import { relative } from 'path'; +import { + relative as relativePath, + sep as pathSep, + posix as posixPath, +} from 'path'; import { ExportedInstance, ApiDoc, @@ -25,6 +29,12 @@ import { TypeLink, } from './types'; +// Always use unix path separators for the relative file +// paths, since we'll be using those for HTTP URLs. +function relativeLink(basePath: string, filePath: string) { + return relativePath(basePath, filePath).split(pathSep).join(posixPath.sep); +} + /** * The ApiDocGenerator uses the typescript compiler API to build the data structure that * describes a Backstage API and all of it's related types. @@ -58,7 +68,7 @@ export default class ApiDocGenerator { const id = this.getObjectPropertyLiteral(info, 'id'); const description = this.getObjectPropertyLiteral(info, 'description'); - const file = relative(this.basePath, source.fileName); + const file = relativeLink(this.basePath, source.fileName); const { line } = source.getLineAndCharacterOfPosition( apiInstance.node.getStart(), ); @@ -111,7 +121,7 @@ export default class ApiDocGenerator { const name = (type.aliasSymbol || type.symbol).name; const [declaration] = (type.aliasSymbol || type.symbol).declarations; const sourceFile = declaration.getSourceFile(); - const file = relative(this.basePath, sourceFile.fileName); + const file = relativeLink(this.basePath, sourceFile.fileName); const { line } = sourceFile.getLineAndCharacterOfPosition( declaration.getStart(), ); @@ -234,7 +244,7 @@ export default class ApiDocGenerator { const { line } = sourceFile.getLineAndCharacterOfPosition( declaration.getStart(), ); - const file = relative(this.basePath, sourceFile.fileName); + const file = relativeLink(this.basePath, sourceFile.fileName); const typeInfo = { id: (symbol as any).id, name: symbol.name, From ed5a0e702821d36ef6ffec0ef0582c37869f90ec Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Mon, 24 Aug 2020 00:34:16 -0400 Subject: [PATCH 48/70] docs: add wealthsimple to adopters list (#2090) --- ADOPTERS.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 7a96198529..dc02ad3b85 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,13 +1,14 @@ -| Organization | Contact | Description of Use | -| --------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| Organization | Contact | Description of Use | +| -------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | +| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | From 9939dace2f2de5bcb0685f5d84bd472187d6020a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 24 Aug 2020 14:54:54 +0200 Subject: [PATCH 49/70] fix(e2e): attempt to unbreak e2e tests due to bad rollup-plugin-dts release --- packages/cli/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0291e720d2..b57ee8cb67 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,7 +76,7 @@ "recursive-readdir": "^2.2.2", "replace-in-file": "^6.0.0", "rollup": "2.23.x", - "rollup-plugin-dts": "^1.4.6", + "rollup-plugin-dts": "1.4.11", "rollup-plugin-esbuild": "^2.0.0", "rollup-plugin-image-files": "^1.4.2", "rollup-plugin-peer-deps-external": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 5a42953413..2ad2111e29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20008,10 +20008,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-dts@^1.4.6: - version "1.4.10" - resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.10.tgz#0373c4284a2ba4d2d72df69c289271a816bc2736" - integrity sha512-bL6MBXc8lK7D5b/tYbHaglxs4ZxMQTQilGA6Xm9KQBEj4h9ZwIDlAsvDooGjJ/cOw23r3POTRtSCEyTHxtzHJg== +rollup-plugin-dts@1.4.11: + version "1.4.11" + resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.11.tgz#aedf0b7bb91d51e20b755e2c18e840edfc7af7a1" + integrity sha512-yiScAMKgwH77b44a/IFGgjLsmwSlNfQhEM+eCb2uMrupQMPE1n/12wrnT431+v1u6wYMF1XuHqldh+v/7mTvYA== optionalDependencies: "@babel/code-frame" "^7.10.4" From 9bacbf14cb6eea3abb0a5fc38d7efe938d2297a5 Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Mon, 24 Aug 2020 15:12:33 +0200 Subject: [PATCH 50/70] Update the system model to focus more on the core entities. (#2011) * Update the system model to focus more on the core entities. Now that we have a way of labeling and annotating catalogue entities, we can focus the system model more on the entities themselves. I believe that also makes more sense as a model that is shared across many companies - it's easier to standardize on the core entities, and leave some room to implement further abstraction or categories to individual implementations. * Re-add system and domain conepts. --- .../software-model-core-entities.png | Bin 0 -> 17277 bytes .../features/software-catalog/system-model.md | 129 ++++++++++-------- 2 files changed, 71 insertions(+), 58 deletions(-) create mode 100644 docs/features/software-catalog/software-model-core-entities.png diff --git a/docs/features/software-catalog/software-model-core-entities.png b/docs/features/software-catalog/software-model-core-entities.png new file mode 100644 index 0000000000000000000000000000000000000000..b718b7527c3e5a8ac723f09d294457f8bda6ce44 GIT binary patch literal 17277 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 literal 0 HcmV?d00001 diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md index 4b2884e097..435fe05b48 100644 --- a/docs/features/software-catalog/system-model.md +++ b/docs/features/software-catalog/system-model.md @@ -3,55 +3,24 @@ id: system-model title: System Model --- -We believe that a strong shared understanding and terminology around systems, -software and resources leads to a better Backstage experience. +We believe that a strong shared understanding and terminology around software +and resources leads to a better Backstage experience. _This description originates from [this RFC](https://github.com/spotify/backstage/issues/390). Note that some of the concepts are not yet supported in Backstage._ -## Concepts +## Core Entities -We model our technology using these five concepts (further explained below): +We model software in the Backstage catalogue using these three core entities +(further explained below): -- **Domains** are a high-level grouping of systems -- **Systems** encapsulate the implementation of APIs -- **APIs** are the boundaries between different components and systems -- **Components** are pieces of software +- **Components** are individual pieces of software +- **APIs** are the boundaries between different components - **Resources** are physical or virtual infrastructure needed to operate a - system + component -![Software Ecosystem Model_ Public Github version](https://user-images.githubusercontent.com/24575/77633084-39bcde80-6f4f-11ea-8251-f8df561a3652.png) - -### Domain - -While systems are the basic level of encapsulation for resources, components and -APIs, it is often useful to group a collection of systems that share -terminology, domain models, business purpose, or documentation, i.e. they form a -bounded context. - -For example, it would make sense if the different systems in the “Payments” -domain would come with some documentation on how to accept payments for a new -product or use-case, share the same entity types in their APIs, and integrate -well with each other. - -### System - -With increasing complexity in software, we believe that systems form an -important abstraction level to help us reason about software ecosystems. Systems -are a useful concept in that they allow us to ignore the implementation details -of a certain functionality for consumers, while allowing the owning team to make -changes as they see fit (leading to low coupling). - -A system, in this sense, is a collection of resources and components that -exposes one or several APIs. Components and resources in a system are typically -owned by the same team and are expected to co-evolve. As such, systems usually -consist of at most a handful of components. - -For example, a playlist management system might encapsulate a backend service to -update playlists, a backend service to query them, and a database to store them. -It could expose an RPC API, a daily snapshots dataset, and an event stream of -playlist updates. +![](system-model-core-entities.png) ### Component @@ -60,34 +29,78 @@ backend service or data pipeline (list not exhaustive). A component can be tracked in source control, or use some existing open source or commercial software. -A component can implement APIs for other components to consume. It might depend -on the resources of the system it belongs to, and APIs from other components or -other systems. All other aspects of the component, e.g. any code dependencies, -must be encapsulated. +A component can implement APIs for other components to consume. In turn it +might depend on APIs implemented by other components, or resources that are +attached to it at runtime. ### API -We believe APIs form an important (maybe the most important) abstraction that -allows large software ecosystems to scale. Thus, APIs are a first class citizen -in the Backstage model and the primary way to discover existing functionality in +APIs form an important (maybe the most important) abstraction that allows large +software ecosystems to scale. Thus, APIs are a first class citizen in the +Backstage model and the primary way to discover existing functionality in the ecosystem. -APIs are implemented by components and form boundaries between components and -systems. They might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a -data schema (eg Avro, TFRecord, ...), or as code interfaces. In any case, APIs -exposed by components need to be in a known machine-readable format so we can +APIs are implemented by components and form boundaries between components. They +might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema +(eg Avro, TFRecord, ...), or as code interfaces. In any case, APIs exposed by +components need to be in a known machine-readable format so we can build further tooling and analysis on top. -Some APIs might be exposed by the system, making them available for any other -Spotify component to consume. Those public APIs must be documented and humanly -discoverable in Backstage. +APIs have a visibility: they are either public (making them available for any +other component to consume), restricted (only available to a whitelisted set of +consumers), or private (only available within their system). As public APIs are +going to be the primary way interaction between components, Backstage supports +documenting, indexing and searching all APIs so we can browse them as +developers. ### Resource -Resources are the infrastructure a system needs to operate, like BigTable -databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together with -components and systems will better allow us to visualize resource footprint, and -create tooling around them. +Resources are the infrastructure a component needs to operate at runtime, like +BigTable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together +with components and systems will better allow us to visualize resource +footprint, and create tooling around them. + +## Ecosystem Modeling + +A large catalogue of components, APIs and resources can be highly granular +and hard to understand as a whole. It might thus be convenient to further +categorize these entities using the following (optional) concepts: +* **Systems** are a collection of entities that cooperate to perform some + function +* **Domains** relate entities and systems to part of the business + +### System + +With increasing complexity in software, systems form an important abstraction +level to help us reason about software ecosystems. Systems are a useful concept +in that they allow us to ignore the implementation details of a certain +functionality for consumers, while allowing the owning team to make changes as +they see fit (leading to low coupling). + +A system, in this sense, is a collection of resources and components that +exposes one or several public APIs. The main benefit of modelling a system is +that it hides its resources and private APIs between the components for any +consumers. This means that as the owner, you can evolve the implementation, in +terms of components and resources, without your consumers being able to notice. +Typically, a system will consist of at most a handful of components (see +Domain for a grouping of systems). + +For example, a playlist management system might encapsulate a backend service +to update playlists, a backend service to query them, and a database to store +them. It could expose an RPC API, a daily snapshots dataset, and an event +stream of playlist updates. + +### Domain +While systems are the basic level of encapsulation for related entities, it is +often useful to group a collection of systems that share terminology, domain +models, metrics, KPIs, business purpose, or documentation, i.e. they form a +bounded context. + +For example, it would make sense if the different systems in the “Payments” +domain would come with some documentation on how to accept payments for a new +product or use-case, share the same entity types in their APIs, and integrate +well with each other. Other domains could be “Content Ingestion”, “Ads” or +“Search”. ## Current status From b5ada28f261f0de18a07aa6821d9749dcb441573 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 24 Aug 2020 15:52:44 +0200 Subject: [PATCH 51/70] Added techdocs-ref to backstage entity --- catalog-info.yaml | 1 + plugins/github-actions/scripts/sample.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/catalog-info.yaml b/catalog-info.yaml index 86b67f95cd..4221ee6dc6 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -7,6 +7,7 @@ metadata: annotations: github.com/project-slug: spotify/backstage backstage.io/github-actions-id: spotify/backstage + backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git spec: type: library owner: Spotify diff --git a/plugins/github-actions/scripts/sample.yaml b/plugins/github-actions/scripts/sample.yaml index d582bf9c35..7d9de50463 100644 --- a/plugins/github-actions/scripts/sample.yaml +++ b/plugins/github-actions/scripts/sample.yaml @@ -5,6 +5,7 @@ metadata: description: backstage.io annotations: github.com/project-slug: 'spotify/backstage' + backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git spec: type: website lifecycle: production From 889e1fd4bb929228aedb04f9bf34768acbf87145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 24 Aug 2020 16:37:51 +0200 Subject: [PATCH 52/70] Docs: strategies for adopting Backstage (#2092) * Adopting guidelines * More updates * Tactics * Add metrics * Add MTTR --- docs/README.md | 1 + docs/assets/pop.png | Bin 0 -> 103686 bytes docs/overview/adopting.md | 144 ++++++++++++++++++++++++++++++++++++++ docs/plugins/index.md | 2 +- microsite/i18n/en.json | 5 +- microsite/sidebars.json | 3 +- mkdocs.yml | 1 + 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 docs/assets/pop.png create mode 100644 docs/overview/adopting.md diff --git a/docs/README.md b/docs/README.md index b9e79a9e0a..55e178b691 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,7 @@ better yet, a pull request. - [Architecture and terminology](overview/architecture-terminology.md) - [Roadmap](overview/roadmap.md) - [Vision](overview/vision.md) + - [Strategies for adopting](overview/adopting.md) - Getting started - [Running Backstage locally](getting-started/index.md) - [Installation](getting-started/installation.md) diff --git a/docs/assets/pop.png b/docs/assets/pop.png new file mode 100644 index 0000000000000000000000000000000000000000..441126f33c08cda5006dcfd913f76b027904bbd5 GIT binary patch literal 103686 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ðYy$A#7{R;Q|C`ENYI;6OPe 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 Case study: Inside Spotify we have a team that owns our CI platform. They +> don't only maintain the pipelines and build servers, but also expose their +> product in Backstage through a plugin. Since they also +> [maintain their own API](../plugins/call-existing-api.md), they can improve +> their product by iterating on API and UI in lockstep. Because the plugin +> follows our [platform design guidelines](../dls/design.md) their customers get +> a CI experience that is consistent with other tools on the platform (and users +> don't have to become experts in Jenkins). + +### Tactics + +Example of tactics we have used to evangelize Backstage internally: + +- Arrange "Lunch & Learns" and seminars. Frequently offer teams interested in + Backstage development to come to a seminar where you show, for example, how to + build a plugin from scratch. + +- Embedding. As contributing teams start development of their first plugin it is + often very appreciated to have one person from the central team come over and + "embed" for a Sprint or two. + +- Hack days. Backstage-focused Hackathons or hack days is a fun way to get + people into plugin development. + +- Show & tell meetings. In order to build an internal community around Backstage + we have quarterly meetings where anyone working on Backstage is invited to + present their work. This is a not only a great way to get early feedback, but + also helps coordination between teams that are building overlapping + experiences. + +- Provide metrics. Add instrumentation to your Backstage deployment and make + metrics available to contributing teams. At Spotify we have even gone so far + as sending out weekly digest email showing how usage metrics have changed for + individual plugins. + +- Pro-actively identify new plugins. Reach out to teams that own internal UIs or + platforms that you think would make sense to consolidate into Backstage. + +## Metrics + +These are some of the metrics that you can use to verify if Backstage has a +successful impact on your software development process: + +- **Onboarding time** Time until new engineers are productive. At Spotify we + measure this as the time until the employee has merged their 10th PR (this + metric was down 55% two years after deploying Backstage). + +- **Number of merges per developer/day** Less time spent jumping between + different tools and looking for information means more time to focus on + shipping code. A second level of bottlenecks can be identified if you + categorize contributions by domain (services, web, data, etc). + +- **Deploys to production** Cousin to the metric above: How many times does an + engineer push changes into production. + +- **MTTR** With clear ownership of all the pieces in your micro services + ecosystem and all tools integrated into one place, Backstage makes it quicker + for teams to find the root cause of failures, and fix them. + +- **Context switching** Reducing context switching can help engineers stay in + the "zone". We measure the number of different tools an engineer have to + interact with in order to get a certain job done (e.g. push a change, follow + it into production and validate it did not break anything). + +- **T-shapedness** A + [T-shaped](https://medium.com/@jchyip/why-t-shaped-people-e8706198e437) + engineer is someone that is able to contribute to different domains of + engineering. Teams with T-shaped people have fewer bottlenecks and can + therefore deliver more consistently. Backstage makes it easier to be T-shaped + since tools and infrastructure is consistent between domains, and information + is available centrally. + +- **eNPS** Surveys asking about how productive people feel, how easy it is to + find information and overall satisfaction with internal tools. + +- **Fragmentation** _(Experimental)_ Backstage + [Software Templates](../features/software-templates/index.md) helps drive + standardization in your software ecosystem. By measuring the variance in + technology between different software components it is possible to get a sense + of the overall fragmentation in your ecosystem. Examples could include: + framework versions, languages, deployment methods and various code quality + measurements. + +Additionally, these proxy metrics can be used to validate the success of +Backstage as _the_ platform: + +- Nr of teams that have contributed at least one plugin (currently 63 inside + Spotify) + +- Nr of total plugins (currently 135 inside Spotify) + +- % of contributions coming from outside the central Backstage team (currently + 85% inside Spotify) diff --git a/docs/plugins/index.md b/docs/plugins/index.md index bb8951f959..c5b67bcec4 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -1,6 +1,6 @@ --- id: index -title: Intro +title: Intro to plugins --- Backstage is a single-page application composed of a set of plugins. diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index 698d86a26a..96c874f496 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -177,6 +177,9 @@ "journey": { "title": "journey" }, + "overview/adopting": { + "title": "Strategies for adopting" + }, "overview/architecture-overview": { "title": "Architecture overview" }, @@ -208,7 +211,7 @@ "title": "Existing plugins" }, "plugins/index": { - "title": "Intro" + "title": "Intro to plugins" }, "plugins/plugin-development": { "title": "Plugin Development in Backstage" diff --git a/microsite/sidebars.json b/microsite/sidebars.json index b07f326086..efb76397aa 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -5,7 +5,8 @@ "overview/architecture-overview", "overview/architecture-terminology", "overview/roadmap", - "overview/vision" + "overview/vision", + "overview/adopting" ], "Getting Started": [ "getting-started/index", diff --git a/mkdocs.yml b/mkdocs.yml index 085d83b7cc..e227ab8f9d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ nav: - Architecture and terminology: 'overview/architecture-terminology.md' - Roadmap: 'overview/roadmap.md' - Vision: 'overview/vision.md' + - Strategies for adopting: 'overview/adopting.md' - Getting started: - Running Backstage locally: 'getting-started/index.md' - Installation: 'getting-started/installation.md' From edab8b2ad89e7a90f61b9d6cff82371f20d2d00f Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 24 Aug 2020 17:01:27 +0200 Subject: [PATCH 53/70] fix(template tags): fixes issues with tags specified with uppercase (#2049) * fix(template tags): fixes issues with tags specified with uppercase * adds new validator for metadata tags * fix(template tags): change tags to be lowercase * fix(template tags docs): update docs to include lowercased tags * fix(validator): change back to using isValidDnsLabel for tags * fix(software catalog docs): use lowercase tags in docs --- docs/features/software-catalog/descriptor-format.md | 4 ++-- docs/features/software-templates/adding-templates.md | 4 ++-- .../extending/create-your-own-templater.md | 4 ++-- .../sample-templates/create-react-app/template.yaml | 6 +++--- .../sample-templates/react-ssr-template/template.yaml | 4 ++-- .../sample-templates/springboot-grpc-template/template.yaml | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index a7d57890dc..78dfcf365c 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -370,8 +370,8 @@ metadata: description: Next.js application skeleton for creating isomorphic web applications. tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: cookiecutter diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index b0405cab79..67e9c23605 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -22,8 +22,8 @@ metadata: Next.js application skeleton for creating isomorphic web applications. # some tags to display in the frontend tags: - - Recommended - - React + - recommended + - react spec: # which templater key to use in the templaters builder templater: cookiecutter diff --git a/docs/features/software-templates/extending/create-your-own-templater.md b/docs/features/software-templates/extending/create-your-own-templater.md index ac05eed3b0..a39f87924b 100644 --- a/docs/features/software-templates/extending/create-your-own-templater.md +++ b/docs/features/software-templates/extending/create-your-own-templater.md @@ -116,8 +116,8 @@ metadata: description: Next.js application skeleton for creating isomorphic web applications. tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: handlebars diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml index 958882e9fd..a377d60ccc 100644 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml @@ -5,9 +5,9 @@ metadata: title: Create React App Template description: Create a new CRA website project tags: - - Experimental - - React - - CRA + - experimental + - react + - cra spec: owner: web@example.com templater: cra diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml index 5594a3a919..ee051de6e0 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml @@ -5,8 +5,8 @@ metadata: title: React SSR Template description: Create a website powered with Next.js tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: cookiecutter diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml index 520d0a62ac..c6189fe7c8 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml @@ -5,8 +5,8 @@ metadata: title: Spring Boot GRPC Service description: Create a simple microservice using gRPC and Spring Boot Java tags: - - Recommended - - Java + - recommended + - java spec: owner: service@example.com templater: cookiecutter From e94c1e321eeec57d93f42f6c5664758edbbb7363 Mon Sep 17 00:00:00 2001 From: Jeff Feng <46946747+fengypants@users.noreply.github.com> Date: Mon, 24 Aug 2020 12:54:18 -0400 Subject: [PATCH 54/70] Update siteConfig.js (#2100) Now that we have a Docs tab on backstage.io, seems like the GitHub menu item can go back to linking to the top of the repo. --- microsite/siteConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/siteConfig.js b/microsite/siteConfig.js index f0c8ce5fa5..73a25cd931 100644 --- a/microsite/siteConfig.js +++ b/microsite/siteConfig.js @@ -35,7 +35,7 @@ const siteConfig = { // For no header links in the top nav bar -> headerLinks: [], headerLinks: [ { - href: 'https://github.com/spotify/backstage#backstage', + href: 'https://github.com/spotify/backstage', label: 'GitHub', }, { From b6ba9ed08af63e4c4d7fb503fbed7dbb48b649b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:03:07 +0200 Subject: [PATCH 55/70] workflows: cache node_modules in e2e tests --- .github/workflows/e2e-win.yml | 5 +++++ .github/workflows/e2e.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 5f75f3d9c8..448a61678c 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -36,6 +36,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 45b9d7e2d8..e58299929d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -43,6 +43,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From a4a9777b61188bd62f4885cd854b4406c6e7a6ea Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:04:00 +0200 Subject: [PATCH 56/70] workflows: remove duplicate storybook build already done by chromatic --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 105642e284..0a0a335203 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,3 @@ jobs: - name: verify plugin template run: yarn lerna -- run diff -- --check - - - name: verify storybook - run: yarn workspace storybook build-storybook From 9b514b1875ba8e6083befb1e316101fa3c181fe0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:05:59 +0200 Subject: [PATCH 57/70] workflows: add yarn cache and frozen lockfile install for chromatic --- .../workflows/chromatic-storybook-test.yml | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/chromatic-storybook-test.yml b/.github/workflows/chromatic-storybook-test.yml index e38b494a73..f7edd68ba9 100644 --- a/.github/workflows/chromatic-storybook-test.yml +++ b/.github/workflows/chromatic-storybook-test.yml @@ -14,7 +14,31 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Required to retrieve git history - - run: yarn install && yarn build-storybook + + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - run: yarn install --frozen-lockfile + + - run: yarn build-storybook + - uses: chromaui/action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} From 7cd7e22e54301ec90a960e94d6767c45bc7b0c8e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:48:44 +0200 Subject: [PATCH 58/70] chore(deps): bump @testing-library/jest-dom from 5.10.1 to 5.11.4 (#2095) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.10.1 to 5.11.4. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/master/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v5.10.1...v5.11.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 53 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2ad2111e29..ed00552d62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4326,17 +4326,16 @@ pretty-format "^25.5.0" "@testing-library/jest-dom@^5.10.1": - version "5.10.1" - resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.10.1.tgz#6508a9f007bd74e5d3c0b3135b668027ab663989" - integrity sha512-uv9lLAnEFRzwUTN/y9lVVXVXlEzazDkelJtM5u92PsGkEasmdI+sfzhZHxSDzlhZVTrlLfuMh2safMr8YmzXLg== + version "5.11.4" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.4.tgz#f325c600db352afb92995c2576022b35621ddc99" + integrity sha512-6RRn3epuweBODDIv3dAlWjOEHQLpGJHB2i912VS3JQtsD22+ENInhdDNl4ZZQiViLlIfFinkSET/J736ytV9sw== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^4.2.2" chalk "^3.0.0" - css "^2.2.4" + css "^3.0.0" css.escape "^1.5.1" - jest-diff "^25.1.0" - jest-matcher-utils "^25.1.0" lodash "^4.17.15" redent "^3.0.0" @@ -8801,15 +8800,14 @@ css.escape@1.5.1, css.escape@^1.5.1: resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= -css@^2.2.4: - version "2.2.4" - resolved "https://registry.npmjs.org/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== +css@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== dependencies: - inherits "^2.0.3" + inherits "^2.0.4" source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" + source-map-resolve "^0.6.0" cssesc@^3.0.0: version "3.0.0" @@ -13880,7 +13878,7 @@ jest-css-modules@^2.1.0: dependencies: identity-obj-proxy "3.0.0" -jest-diff@^25.1.0, jest-diff@^25.2.1: +jest-diff@^25.2.1: version "25.5.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== @@ -13957,11 +13955,6 @@ jest-fetch-mock@^3.0.3: cross-fetch "^3.0.4" promise-polyfill "^8.1.3" -jest-get-type@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" - integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw== - jest-get-type@^25.2.6: version "25.2.6" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" @@ -14023,16 +14016,6 @@ jest-leak-detector@^26.0.1: jest-get-type "^26.0.0" pretty-format "^26.0.1" -jest-matcher-utils@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220" - integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ== - dependencies: - chalk "^3.0.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" - jest-matcher-utils@^26.0.1: version "26.0.1" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz#12e1fc386fe4f14678f4cc8dbd5ba75a58092911" @@ -18329,7 +18312,7 @@ pretty-format@^24.3.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.5.0: +pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== @@ -20764,7 +20747,7 @@ source-list-map@^2.0.0: resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: +source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -20775,6 +20758,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" From 80089ed32eb9909f9965ab48543bb550d55f89fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:49:13 +0200 Subject: [PATCH 59/70] chore(deps-dev): bump @types/supertest from 2.0.9 to 2.0.10 (#2094) Bumps [@types/supertest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/supertest) from 2.0.9 to 2.0.10. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/supertest) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ed00552d62..857b8b2cc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5264,9 +5264,9 @@ "@types/node" "*" "@types/supertest@^2.0.8": - version "2.0.9" - resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.9.tgz#049bddbcb0ee0d60a9b836ccc977d813a1c32325" - integrity sha512-0BTpWWWAO1+uXaP/oA0KW1eOZv4hc0knhrWowV06Gwwz3kqQxNO98fUFM2e15T+PdPRmOouNFrYvaBgdojPJ3g== + version "2.0.10" + resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz#630d79b4d82c73e043e43ff777a9ca98d457cab7" + integrity sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ== dependencies: "@types/superagent" "*" From ce6205146134d92741d0986f119d747ac381be6d Mon Sep 17 00:00:00 2001 From: Joel Low Date: Mon, 24 Aug 2020 16:59:09 +0800 Subject: [PATCH 60/70] feat: support specifying lint output format --- packages/cli/src/commands/lint.ts | 2 +- packages/cli/src/index.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index b355939e55..7b4532ffa7 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -22,7 +22,7 @@ export default async (cmd: Command, cmdArgs: string[]) => { const args = [ '--ext=js,jsx,ts,tsx', '--max-warnings=0', - '--format=eslint-formatter-friendly', + `--format=${cmd.format}`, ...(cmdArgs ?? [paths.targetDir]), ]; if (cmd.fix) { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4b9dc4bab7..742e05f34a 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -113,6 +113,11 @@ const main = (argv: string[]) => { program .command('lint') + .option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ) .option('--fix', 'Attempt to automatically fix violations') .description('Lint a package') .action(lazyAction(() => import('./commands/lint'), 'default')); From e323e1691b03f869253e2429aae8f5b77054641b Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 25 Aug 2020 12:07:12 +0200 Subject: [PATCH 61/70] update tags to be lowercased in docs template (#2104) * update tags to be lowercased in docs template * Update template.yaml --- .../sample-templates/docs-template/template.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml index 01ebdadbce..da2152280d 100644 --- a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml @@ -5,9 +5,9 @@ metadata: title: Documentation Template description: Create a new standalone documentation project tags: - - Experimental - - TechDocs - - MkDocs + - experimental + - techdocs + - mkdocs spec: owner: spotify/techdocs-core templater: cookiecutter @@ -26,4 +26,4 @@ spec: title: Description type: string description: Description of the component - \ No newline at end of file + From d42c09c0586b4dded79c1d5798a91001a8b3eb0b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 12:13:09 +0200 Subject: [PATCH 62/70] workflows: use new multi-path caching to completely cache node_modules + prep for composite action --- .../workflows/chromatic-storybook-test.yml | 40 ++++++++------ .github/workflows/ci.yml | 54 ++++++++++++------- .github/workflows/e2e-win.yml | 38 +++++++------ .github/workflows/e2e.yml | 38 +++++++------ .github/workflows/master-win.yml | 38 +++++++------ .github/workflows/master.yml | 37 +++++++------ .github/workflows/microsite-build-check.yml | 38 ++++++------- .../microsite-with-storybook-deploy.yml | 38 ++++++------- 8 files changed, 188 insertions(+), 133 deletions(-) diff --git a/.github/workflows/chromatic-storybook-test.yml b/.github/workflows/chromatic-storybook-test.yml index f7edd68ba9..0bcaf400d0 100644 --- a/.github/workflows/chromatic-storybook-test.yml +++ b/.github/workflows/chromatic-storybook-test.yml @@ -15,27 +15,33 @@ jobs: with: fetch-depth: 0 # Required to retrieve git history - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - - run: yarn install --frozen-lockfile + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install + run: yarn install --frozen-lockfile + # End of yarn setup - run: yarn build-storybook diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a0a335203..9d0a37cd1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,34 +20,52 @@ jobs: - uses: actions/checkout@v2 - name: fetch branch master run: git fetch origin master - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows. + # TODO(Rugvip): move this to composite action once all features we use are supported - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + + # Cache every node_modules folder inside the monorepo + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + # We use both yarn.lock and package.json as cache keys to ensure that + # changes to local monorepo packages bust the cache. + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + + # If we get a cache hit for node_modules, there's no need to bring in the global + # yarn cache or run yarn install, as all dependencies will be installed already. + + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: yarn install + if: steps.cache-modules.outputs.cache-hit != 'true' + run: yarn install --frozen-lockfile + # End of yarn setup - name: check for yarn.lock changes id: yarn-lock run: git diff --quiet origin/master HEAD -- yarn.lock continue-on-error: true - - name: yarn install - run: yarn install --frozen-lockfile - - name: verify doc links run: node docs/verify-links.js diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 448a61678c..37cb13ffbe 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -26,27 +26,35 @@ jobs: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + - run: yarn tsc - name: yarn build run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e58299929d..401fe7297a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -33,27 +33,35 @@ jobs: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + - run: yarn tsc - name: yarn build run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index 074f944250..b5a042a236 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -18,29 +18,35 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + # Tests are broken on Windows, disabled for now # - name: test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 805095ec53..04b50c3e60 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -18,29 +18,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: lint run: yarn lerna -- run lint diff --git a/.github/workflows/microsite-build-check.yml b/.github/workflows/microsite-build-check.yml index 41f43399ca..dc86ca2630 100644 --- a/.github/workflows/microsite-build-check.yml +++ b/.github/workflows/microsite-build-check.yml @@ -21,32 +21,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: build microsite run: yarn workspace backstage-microsite build diff --git a/.github/workflows/microsite-with-storybook-deploy.yml b/.github/workflows/microsite-with-storybook-deploy.yml index 84ac6f9490..2c8b8af40c 100644 --- a/.github/workflows/microsite-with-storybook-deploy.yml +++ b/.github/workflows/microsite-with-storybook-deploy.yml @@ -25,32 +25,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: build microsite run: yarn workspace backstage-microsite build From ff888ebc8c667a5e5eead8c21f12c81a1d928f79 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 13:19:39 +0200 Subject: [PATCH 63/70] cli-common: remove dev dep on cli --- packages/cli-common/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 80cc665426..5e050210da 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -29,7 +29,6 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.19", "@types/jest": "^26.0.7", "@types/node": "^12.0.0" }, From bc883377fd1f4352b78c3e0e4bfbba196c2af32e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 13:22:54 +0200 Subject: [PATCH 64/70] workflows: no need to include deps in build for tests --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 105642e284..610a1cd7a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,8 +59,7 @@ jobs: - name: build changed packages if: ${{ steps.yarn-lock.outcome == 'success' }} - # Need to build all dependencies as well to be able to run tests later - run: yarn lerna -- run build --since origin/master --include-dependencies + run: yarn lerna -- run build --since origin/master - name: build all packages if: ${{ steps.yarn-lock.outcome == 'failure' }} From 4665a2f5d46335b23d36bb54ebbba886808ce278 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 25 Aug 2020 13:57:57 +0200 Subject: [PATCH 65/70] Added selectedTabId param to generatePath in JobStatusModal (#2109) * Added selectedTabId param to generatePath in JobStatusModal * Added selectedTabId to generatePath in ApiCatalogTable * Link to default entity tab in JobStatusModal --- .../src/components/ApiCatalogTable/ApiCatalogTable.tsx | 1 + .../src/components/JobStatusModal/JobStatusModal.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx b/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx index e30583caed..99848637b6 100644 --- a/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx +++ b/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx @@ -37,6 +37,7 @@ const columns: TableColumn[] = [ .filter(Boolean) .join(':'), kind: entity.kind, + selectedTabId: 'overview', })} > {entity.metadata.name} diff --git a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx index c6ed55c92c..f5a592b7b7 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx +++ b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx @@ -26,7 +26,7 @@ import { useJobPolling } from './useJobPolling'; import { Job } from '../../types'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { Button } from '@backstage/core'; -import { entityRoute } from '@backstage/plugin-catalog'; +import { entityRouteDefault } from '@backstage/plugin-catalog'; import { generatePath } from 'react-router-dom'; type Props = { @@ -72,7 +72,7 @@ export const JobStatusModal = ({ {entity && (