From a971957d5023ad8f1bd5f033c0c955f6b73d74ad Mon Sep 17 00:00:00 2001 From: Pepijn Schildkamp Date: Fri, 16 Jul 2021 20:53:11 +0200 Subject: [PATCH 001/250] Add authHandler and login resolver for OAuth2 provider Signed-off-by: Pepijn Schildkamp --- plugins/auth-backend/src/providers/index.ts | 1 + .../src/providers/oauth2/index.ts | 2 +- .../src/providers/oauth2/provider.test.ts | 93 ++++++++ .../src/providers/oauth2/provider.ts | 202 ++++++++++++++---- 4 files changed, 255 insertions(+), 43 deletions(-) create mode 100644 plugins/auth-backend/src/providers/oauth2/provider.test.ts diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 6b262dcf68..576c5011af 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -16,6 +16,7 @@ export * from './google'; export * from './microsoft'; +export * from './oauth2'; export { factories as defaultAuthProviderFactories } from './factories'; // Export the minimal interface required for implementing a diff --git a/plugins/auth-backend/src/providers/oauth2/index.ts b/plugins/auth-backend/src/providers/oauth2/index.ts index d68208a148..4e2509735d 100644 --- a/plugins/auth-backend/src/providers/oauth2/index.ts +++ b/plugins/auth-backend/src/providers/oauth2/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { createOAuth2Provider } from './provider'; +export { createOAuth2Provider, oAuth2EmailSignInResolver } from './provider'; export type { OAuth2ProviderOptions } from './provider'; diff --git a/plugins/auth-backend/src/providers/oauth2/provider.test.ts b/plugins/auth-backend/src/providers/oauth2/provider.test.ts new file mode 100644 index 0000000000..773471c42f --- /dev/null +++ b/plugins/auth-backend/src/providers/oauth2/provider.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { OAuth2AuthProvider } from './provider'; +import * as helpers from '../../lib/passport/PassportStrategyHelper'; +import { OAuthResult } from '../../lib/oauth'; +import { getVoidLogger } from '@backstage/backend-common'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; + +const mockFrameHandler = (jest.spyOn( + helpers, + 'executeFrameHandlerStrategy', +) as unknown) as jest.MockedFunction< + () => Promise<{ result: OAuthResult; privateInfo: any }> +>; + +describe('createOAuth2Provider', () => { + it('should auth', async () => { + const tokenIssuer = { + issueToken: jest.fn(), + listPublicKeys: jest.fn(), + }; + const catalogIdentityClient = { + findUser: jest.fn(), + }; + + const provider = new OAuth2AuthProvider({ + logger: getVoidLogger(), + catalogIdentityClient: (catalogIdentityClient as unknown) as CatalogIdentityClient, + tokenIssuer: (tokenIssuer as unknown) as TokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: { + email: fullProfile.emails![0]!.value, + displayName: fullProfile.displayName, + picture: 'http://backstage.io/lols', + }, + }), + clientId: 'mock', + clientSecret: 'mock', + callbackUrl: 'mock', + authorizationUrl: 'mock', + tokenUrl: 'mock', + }); + + mockFrameHandler.mockResolvedValueOnce({ + result: { + fullProfile: { + emails: [{ value: 'conrad@example.com' }], + displayName: 'Conrad', + id: 'conrad', + provider: 'oAuth2', + }, + params: { + id_token: 'idToken', + scope: 'scope', + expires_in: 123, + }, + accessToken: 'accessToken', + }, + privateInfo: { + refreshToken: 'wacka', + }, + }); + const { response } = await provider.handler({} as any); + expect(response).toEqual({ + providerInfo: { + accessToken: 'accessToken', + expiresInSeconds: 123, + idToken: 'idToken', + scope: 'scope', + }, + profile: { + email: 'conrad@example.com', + displayName: 'Conrad', + picture: 'http://backstage.io/lols', + }, + }); + }); +}); diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index f174543a63..7e09487c32 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -18,15 +18,15 @@ import express from 'express'; import passport from 'passport'; import { Strategy as OAuth2Strategy } from 'passport-oauth2'; import { - OAuthAdapter, - OAuthProviderOptions, - OAuthHandlers, - OAuthResponse, - OAuthEnvironmentHandler, - OAuthStartRequest, encodeState, + OAuthAdapter, + OAuthEnvironmentHandler, + OAuthHandlers, + OAuthProviderOptions, OAuthRefreshRequest, + OAuthResponse, OAuthResult, + OAuthStartRequest, } from '../../lib/oauth'; import { executeFetchUserProfileStrategy, @@ -36,22 +36,46 @@ import { makeProfileInfo, PassportDoneCallback, } from '../../lib/passport'; -import { RedirectInfo, AuthProviderFactory } from '../types'; +import { + AuthHandler, + AuthProviderFactory, + RedirectInfo, + SignInResolver, +} from '../types'; +import { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog'; +import { TokenIssuer } from '../../identity'; +import { Logger } from 'winston'; type PrivateInfo = { refreshToken: string; }; export type OAuth2AuthProviderOptions = OAuthProviderOptions & { + signInResolver?: SignInResolver; + authHandler: AuthHandler; + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; authorizationUrl: string; tokenUrl: string; scope?: string; + logger: Logger; }; export class OAuth2AuthProvider implements OAuthHandlers { private readonly _strategy: OAuth2Strategy; + private readonly signInResolver?: SignInResolver; + private readonly authHandler: AuthHandler; + private readonly tokenIssuer: TokenIssuer; + private readonly catalogIdentityClient: CatalogIdentityClient; + private readonly logger: Logger; constructor(options: OAuth2AuthProviderOptions) { + this.signInResolver = options.signInResolver; + this.authHandler = options.authHandler; + this.tokenIssuer = options.tokenIssuer; + this.catalogIdentityClient = options.catalogIdentityClient; + this.logger = options.logger; + this._strategy = new OAuth2Strategy( { clientID: options.clientId, @@ -102,18 +126,8 @@ export class OAuth2AuthProvider implements OAuthHandlers { PrivateInfo >(req, this._strategy); - const profile = makeProfileInfo(result.fullProfile, result.params.id_token); - return { - response: await this.populateIdentity({ - profile, - providerInfo: { - idToken: result.params.id_token, - accessToken: result.accessToken, - scope: result.params.scope, - expiresInSeconds: result.params.expires_in, - }, - }), + response: await this.handleResult(result), refreshToken: privateInfo.refreshToken, }; } @@ -130,46 +144,124 @@ export class OAuth2AuthProvider implements OAuthHandlers { refreshToken: updatedRefreshToken, } = refreshTokenResponse; - const rawProfile = await executeFetchUserProfileStrategy( + const fullProfile = await executeFetchUserProfileStrategy( this._strategy, accessToken, ); - const profile = makeProfileInfo(rawProfile, params.id_token); - return this.populateIdentity({ - providerInfo: { - accessToken, - refreshToken: updatedRefreshToken, - idToken: params.id_token, - expiresInSeconds: params.expires_in, - scope: params.scope, - }, - profile, + return this.handleResult({ + fullProfile, + params, + accessToken, + refreshToken: updatedRefreshToken, }); } - // Use this function to grab the user profile info from the token - // Then populate the profile with it - private async populateIdentity( - response: OAuthResponse, - ): Promise { - const { profile } = response; + private async handleResult(result: OAuthResult) { + const { profile } = await this.authHandler(result); - if (!profile.email) { - throw new Error('Profile does not contain an email'); + const response: OAuthResponse = { + providerInfo: { + idToken: result.params.id_token, + accessToken: result.accessToken, + scope: result.params.scope, + expiresInSeconds: result.params.expires_in, + }, + profile, + }; + + if (this.signInResolver) { + response.backstageIdentity = await this.signInResolver( + { + result, + profile, + }, + { + tokenIssuer: this.tokenIssuer, + catalogIdentityClient: this.catalogIdentityClient, + logger: this.logger, + }, + ); } - const id = profile.email.split('@')[0]; - return { ...response, backstageIdentity: { id } }; + return response; } } -export type OAuth2ProviderOptions = {}; +export const oAuth2EmailSignInResolver: SignInResolver = async ( + info, + ctx, +) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('OAuth2 profile contained no email'); + } + + const entity = await ctx.catalogIdentityClient.findUser({ + annotations: { + 'oauth2/email': profile.email, + }, + }); + + const claims = getEntityClaims(entity); + const token = await ctx.tokenIssuer.issueToken({ claims }); + + return { id: entity.metadata.name, entity, token }; +}; + +export const oAuth2DefaultSignInResolver: SignInResolver = async ( + info, + ctx, +) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('Profile contained no email'); + } + + let userId: string; + + try { + const entity = await ctx.catalogIdentityClient.findUser({ + annotations: { + 'oauth2/email': profile.email, + }, + }); + userId = entity.metadata.name; + } catch (error) { + ctx.logger.warn( + `Failed to look up user, ${error}, falling back to allowing login based on email pattern, this will probably break in the future`, + ); + userId = profile.email.split('@')[0]; + } + + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: userId, ent: [`user:default/${userId}`] }, + }); + + return { id: userId, token }; +}; + +export type OAuth2ProviderOptions = { + authHandler?: AuthHandler; + + signIn?: { + resolver?: SignInResolver; + }; +}; export const createOAuth2Provider = ( - _options?: OAuth2ProviderOptions, + options?: OAuth2ProviderOptions, ): AuthProviderFactory => { - return ({ providerId, globalConfig, config, tokenIssuer }) => + return ({ + providerId, + globalConfig, + config, + tokenIssuer, + catalogApi, + logger, + }) => OAuthEnvironmentHandler.mapConfig(config, envConfig => { const clientId = envConfig.getString('clientId'); const clientSecret = envConfig.getString('clientSecret'); @@ -178,13 +270,39 @@ export const createOAuth2Provider = ( const tokenUrl = envConfig.getString('tokenUrl'); const scope = envConfig.getOptionalString('scope'); + const catalogIdentityClient = new CatalogIdentityClient({ + catalogApi, + tokenIssuer, + }); + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const signInResolverFn = + options?.signIn?.resolver ?? oAuth2DefaultSignInResolver; + + const signInResolver: SignInResolver = info => + signInResolverFn(info, { + catalogIdentityClient, + tokenIssuer, + logger, + }); + const provider = new OAuth2AuthProvider({ clientId, clientSecret, + tokenIssuer, + catalogIdentityClient, callbackUrl, + signInResolver, + authHandler, authorizationUrl, tokenUrl, scope, + logger, }); return OAuthAdapter.fromConfig(globalConfig, provider, { From e9b1e2a9f40950b955a15c55109c7863701b95a5 Mon Sep 17 00:00:00 2001 From: Pepijn Schildkamp Date: Fri, 16 Jul 2021 21:04:55 +0200 Subject: [PATCH 002/250] Added changeset for oAuth2 provider refactoring Signed-off-by: Pepijn Schildkamp --- .changeset/strong-ravens-smoke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/strong-ravens-smoke.md diff --git a/.changeset/strong-ravens-smoke.md b/.changeset/strong-ravens-smoke.md new file mode 100644 index 0000000000..8579e74d37 --- /dev/null +++ b/.changeset/strong-ravens-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Added signIn and authHandler resolver for oAuth2 provider From f9efb38a969b66d33601434c7a7296315c4a4093 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 10 Aug 2021 21:13:17 +0200 Subject: [PATCH 003/250] scaffolder-backend: remove sample-templates Closes https://github.com/backstage/backstage/issues/6415 Signed-off-by: Himanshu Mishra --- .../create-react-app/skeleton/.gitignore | 23 -- .../create-react-app/skeleton/README.md | 46 ---- .../skeleton/catalog-info.yaml | 12 - .../create-react-app/skeleton/docs/index.md | 0 .../create-react-app/skeleton/mkdocs.yml | 8 - .../create-react-app/skeleton/package.json | 43 ---- .../skeleton/public/favicon.ico | Bin 3870 -> 0 bytes .../skeleton/public/index.html | 43 ---- .../skeleton/public/logo192.png | Bin 5347 -> 0 bytes .../skeleton/public/logo512.png | Bin 9664 -> 0 bytes .../skeleton/public/manifest.json | 25 -- .../skeleton/public/robots.txt | 3 - .../create-react-app/skeleton/src/App.css | 38 --- .../skeleton/src/App.test.tsx | 9 - .../create-react-app/skeleton/src/App.tsx | 26 --- .../create-react-app/skeleton/src/index.css | 13 -- .../create-react-app/skeleton/src/index.tsx | 17 -- .../create-react-app/skeleton/src/logo.svg | 1 - .../skeleton/src/react-app-env.d.ts | 1 - .../skeleton/src/reportWebVitals.ts | 15 -- .../skeleton/src/setupTests.ts | 5 - .../create-react-app/skeleton/tsconfig.json | 26 --- .../create-react-app/template.yaml | 77 ------ .../docs-template/skeleton/catalog-info.yaml | 12 - .../docs-template/skeleton/docs/index.md | 28 --- .../docs-template/skeleton/mkdocs.yml | 8 - .../docs-template/template.yaml | 86 ------- .../pull-request/template.yaml | 77 ------ .../pull-request/template/catalog-info.yaml | 8 - .../sample-templates/rails-demo/template.yaml | 174 -------------- .../template/rails-template-file.rb | 14 -- .../react-ssr-template/skeleton/.editorconfig | 13 -- .../react-ssr-template/skeleton/.eslintignore | 1 - .../react-ssr-template/skeleton/.eslintrc.js | 1 - .../skeleton/.github/workflows/build.yml | 41 ---- .../react-ssr-template/skeleton/.gitignore | 18 -- .../react-ssr-template/skeleton/README.md | 14 -- .../skeleton/babel.config.js | 4 - .../skeleton/catalog-info.yaml | 12 - .../react-ssr-template/skeleton/docs/index.md | 28 --- .../skeleton/jest.config.js | 8 - .../react-ssr-template/skeleton/mkdocs.yml | 8 - .../react-ssr-template/skeleton/next-env.d.ts | 5 - .../skeleton/next.config.js | 3 - .../react-ssr-template/skeleton/package.json | 52 ----- .../skeleton/prettier.config.js | 1 - .../skeleton/public/static/fonts.css | 0 .../skeleton/src/__tests__/index.test.tsx | 13 -- .../skeleton/src/components/Header.tsx | 3 - .../skeleton/src/pages/_app.tsx | 37 --- .../skeleton/src/pages/_document.tsx | 41 ---- .../skeleton/src/pages/api/ping.ts | 5 - .../skeleton/src/pages/index.tsx | 5 - .../react-ssr-template/skeleton/tsconfig.json | 20 -- .../react-ssr-template/template.yaml | 77 ------ .../skeleton/.editorconfig | 10 - .../skeleton/.github/workflows/build.yml | 21 -- .../skeleton/.gitignore | 7 - .../skeleton/Dockerfile | 5 - .../skeleton/README.md | 3 - .../skeleton/SERVICE.marker | 1 - .../skeleton/catalog-info.yaml | 12 - .../skeleton/docs/index.md | 28 --- .../skeleton/mkdocs.yml | 8 - .../springboot-grpc-template/skeleton/pom.xml | 221 ------------------ .../Application.java | 12 - .../GreeterService.java | 19 -- .../src/main/resources/application.properties | 3 - .../skeleton/src/main/resources/banner.txt | 22 -- .../skeleton/src/main/resources/greeter.proto | 17 -- .../ApplicationTest.java | 15 -- .../skeleton/{{cookiecutter.artifact_id}}.sh | 2 - .../springboot-grpc-template/template.yaml | 88 ------- .../v1beta2-demo/template.yaml | 107 --------- .../v1beta2-demo/template/catalog-info.yaml | 12 - 75 files changed, 1861 deletions(-) delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/docs/index.md delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo512.png delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json delete mode 100644 plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md delete mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/pull-request/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/public/static/fonts.css delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java delete mode 100755 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh delete mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml delete mode 100644 plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore deleted file mode 100644 index 4d29575de8..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md deleted file mode 100644 index 305d413b8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `yarn start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.\ -You will also see any lint errors in the console. - -### `yarn test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `yarn build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `yarn eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (`webpack`, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml deleted file mode 100644 index df23b90b97..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/docs/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json deleted file mode 100644 index 2ce9bf6266..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "{{cookiecutter.component_id}}", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.1.0", - "@testing-library/user-event": "^12.1.10", - "@types/jest": "^26.0.15", - "@types/node": "^12.0.0", - "@types/react": "^17.0.0", - "@types/react-dom": "^17.0.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "typescript": "^4.1.2", - "web-vitals": "^1.0.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/favicon.ico deleted file mode 100644 index a11777cc471a4344702741ab1c8a588998b1311a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html deleted file mode 100644 index aa069f27cb..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - React App - - - -
- - - diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/logo192.png deleted file mode 100644 index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json deleted file mode 100644 index 080d6c77ac..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt deleted file mode 100644 index e9e57dc4d4..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css deleted file mode 100644 index 74b5e05345..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx deleted file mode 100644 index 2a68616d98..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx deleted file mode 100644 index a53698aab3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/App.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( - - ); -} - -export default App; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css deleted file mode 100644 index ec2585e8c0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx deleted file mode 100644 index ef2edf8ea3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -ReactDOM.render( - - - , - document.getElementById('root') -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg deleted file mode 100644 index 9dfc1c058c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5fc6..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16e0f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts deleted file mode 100644 index 8f2609b7b3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json b/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json deleted file mode 100644 index a273b0cfc0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/skeleton/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml deleted file mode 100644 index 664b405f29..0000000000 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: create-react-app-template - title: Create React App Template - description: Create a new CRA website project - tags: - - react - - cra -spec: - owner: web@example.com - type: website - parameters: - - title: Provide some simple information - required: - - component_id - - owner - properties: - component_id: - title: Name - type: string - description: Unique name of the component - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml deleted file mode 100644 index 8f18e06d7e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: documentation - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md deleted file mode 100644 index b6f7344334..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -## {{ cookiecutter.name }} - -{{ 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/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml deleted file mode 100644 index 7e1c015f28..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.name | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml deleted file mode 100644 index 1e81242e7a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: docs-template - title: Documentation Template - description: Create a new standalone documentation project - tags: - - recommended - - techdocs - - mkdocs -spec: - owner: backstage/techdocs-core - type: documentation - - parameters: - - title: Fill in some steps - required: - - name - - description - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - description: - title: Description - type: string - description: A description for the component - ui:options: - rows: 5 - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - # This template is meant to be used on top of an existing tmeplate. - # By adding the following and fetching from an absolute URL you can - # add in the docs template - steps: - - id: fetch - name: Template Docs Skeleton - action: fetch:cookiecutter - input: - url: ./skeleton - values: - name: '{{ parameters.name }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml deleted file mode 100644 index b6de1c29ca..0000000000 --- a/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: pull-request - title: Pull Request Action template - description: scaffolder v1beta2 template demo publishing to PR on existing git repository -spec: - owner: backstage/techdocs-core - type: service - - parameters: - - title: Fill in some steps - required: - - name - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - description: - title: Description - type: string - description: Description of the component - targetPath: - title: Target Path in repo - type: string - description: Name of the directory to create in the repository - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - steps: - - id: fetch-base - name: Fetch Base - action: fetch:cookiecutter - input: - url: ./template - values: - name: '{{parameters.name}}' - - - id: fetch-docs - name: Fetch Docs - action: fetch:plain - input: - targetPath: ./community - url: https://github.com/backstage/community/tree/main/backstage-community-sessions - - - id: publish - name: Publish - action: publish:github:pull-request - input: - repoUrl: '{{ parameters.repoUrl }}' - title: 'Create new project: {{parameters.name}}' - branchName: 'create-{{parameters.name}}' - description: | - # New project: {{parameters.name}} - - {{#if parameters.description}} - {{parameters.description}} - {{/if}} - targetPath: '{{#if parameters.targetPath}}{{parameters.targetPath}}{{else}}{{parameters.name}}{{/if}}' - - output: - links: - - url: '{{steps.publish.output.remoteUrl}}' - text: 'Go to PR' diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml deleted file mode 100644 index 04f055bd46..0000000000 --- a/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} -spec: - type: website - lifecycle: experimental - owner: user:guest diff --git a/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml deleted file mode 100644 index 174c58741b..0000000000 --- a/plugins/scaffolder-backend/sample-templates/rails-demo/template.yaml +++ /dev/null @@ -1,174 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: rails-demo - title: Rails template - description: scaffolder Rails app -spec: - owner: backstage/techdocs-core - type: service - - parameters: - - title: Fill in some steps - required: - - name - - owner - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - system: - title: System - type: string - description: System of the component - ui:field: EntityPicker - ui:options: - allowedKinds: - - System - defaultKind: System - - - title: Choose a location - required: - - repoUrl - - dryRun - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - dryRun: - title: Only perform a dry run, don't publish anything - type: boolean - default: false - railsArguments: - title: arguments to run the rails new command - type: object - properties: - minimal: - title: minimal - description: Preconfigure a minimal rails app - type: boolean - skipBundle: - title: skipBundle - description: Don't run bundle install - type: boolean - skipWebpackInstall: - title: skipWebpackInstall - description: Don't run Webpack install - type: boolean - api: - title: api - description: Preconfigure smaller stack for API only apps - type: boolean - template: - title: template - description: Path to some application template (can be a filesystem path or URL) - type: string - default: './rails-template-file.rb' - webpacker: - title: webpacker - description: 'Preconfigure Webpack with a particular framework (options: react, - vue, angular, elm, stimulus)' - type: string - enum: - - react - - vue - - angular - - elm - - stimulus - database: - title: database - description: 'Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)' - type: string - enum: - - mysql - - postgresql - - sqlite3 - - oracle - - sqlserver - - jdbcmysql - - jdbcsqlite3 - - jdbcpostgresql - - jdbc - railsVersion: - title: Rails version in Gemfile - description: 'Set up the application with Gemfile pointing to a specific version - (options: dev, edge, master)' - type: string - enum: - - dev - - edge - - master - - steps: - - id: fetch-base - name: Fetch Base - action: fetch:rails - input: - url: ./template - values: - name: '{{ parameters.name }}' - owner: '{{ parameters.owner }}' - system: '{{ parameters.system }}' - railsArguments: '{{ json parameters.railsArguments }}' - - - name: Write Catalog information - action: catalog:write - input: - component: - apiVersion: 'backstage.io/v1alpha1' - kind: Component - metadata: - name: '{{ parameters.name }}' - annotations: - github.com/project-slug: '{{ projectSlug parameters.repoUrl }}' - spec: - type: service - lifecycle: production - owner: '{{ parameters.owner }}' - - - id: publish - if: '{{ not parameters.dryRun }}' - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.name }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - if: '{{ not parameters.dryRun }}' - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - - name: Results - if: '{{ parameters.dryRun }}' - action: debug:log - input: - listWorkspace: true - - output: - links: - - title: Repository - url: '{{ steps.publish.output.remoteUrl }}' - - title: Open in catalog - icon: 'catalog' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb b/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb deleted file mode 100644 index 90c0d53ddc..0000000000 --- a/plugins/scaffolder-backend/sample-templates/rails-demo/template/rails-template-file.rb +++ /dev/null @@ -1,14 +0,0 @@ -gem_group :development, :test do - gem "rspec" - gem "rspec-rails" -end - -rakefile("example.rake") do - <<-TASK - namespace :example do - task :backstage do - puts "i like backstage!" - end - end - TASK -end diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig deleted file mode 100644 index 23c3268a0f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -insert_final_newline = false \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore deleted file mode 100644 index 8b13789179..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js deleted file mode 100644 index 8623614899..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@spotify/web-scripts/config/eslintrc.js'); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml deleted file mode 100644 index 907da7e820..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Frontend CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x] - - steps: - - name: checkout code - uses: actions/checkout@v1 - - name: get yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: yarn install, build, and test - working-directory: . - run: | - yarn install - yarn build - yarn test - env: - CI: true diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore deleted file mode 100644 index 4d1e0d9635..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage -junit.xml - -# build -/build - -# misc -.DS_Store - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md deleted file mode 100644 index 415065179b..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# {{ cookiecutter.component_id }} - -## Description - -{{ cookiecutter.description }} - -## Develop - -```bash -# install dependencies -$ yarn -# start development server -$ yarn dev -``` \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js deleted file mode 100644 index db2d280677..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/babel.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - plugins: ['babel-plugin-styled-components'], - presets: ['next/babel', '@zeit/next-typescript/babel'], -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml deleted file mode 100644 index df23b90b97..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md deleted file mode 100644 index 2a0f572ff2..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -# {{ cookiecutter.component_id }} - -{{ cookiecutter.description }} - -## Getting started - -Start writing 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/react-ssr-template/skeleton/jest.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js deleted file mode 100644 index 0dc2a9c0ca..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - ...require('@spotify/web-scripts/config/jest.config.js'), - testEnvironment: 'jsdom', - testPathIgnorePatterns: ['/node_modules/', '/build/'], - transform: { - '^.+\\.tsx?$': 'babel-jest', - }, -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts deleted file mode 100644 index fd10310f68..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// read more about this file here ---> https://github.com/zeit/next.js/blob/canary/docs/basic-features/typescript.md -/* eslint spaced-comment: ["error", "always", { "markers": ["/"] }] */ - -/// -/// diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js deleted file mode 100644 index be8b159307..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/next.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - distDir: 'build', -}; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json deleted file mode 100644 index 75925bf567..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "{{ cookiecutter.component_id }}", - "version": "0.0.0", - "description": "{{ cookiecutter.description }}", - "license": "UNLICENSED", - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start", - "lint": "web-scripts lint --ignore-path=.gitignore", - "test": "web-scripts test --config jest.config.js", - "pretest:ci": "yarn lint", - "test:ci": "yarn test --ci --coverage --reporters=default --reporters=jest-junit" - }, - "dependencies": { - "@zeit/next-typescript": "^1.1.1", - "babel-plugin-styled-components": "^1.10.6", - "next": "^9.1.1", - "react": "^16.8.5", - "react-dom": "^16.8.5", - "styled-components": "^4.3.2" - }, - "devDependencies": { - "@spotify/tsconfig": "^5.0.0", - "@spotify/web-scripts": "^5.0.0", - "@testing-library/react": "^8.0.1", - "@types/node": "^14.14.32", - "@types/react": "^16.8.7", - "@types/react-dom": "^16.8.2", - "@types/styled-components": "^4.1.18", - "husky": "^2.7.0", - "jest-junit": "^8.0.0", - "typescript": "^3.9.2" - }, - "husky": { - "hooks": { - "pre-commit": "web-scripts precommit" - } - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js deleted file mode 100644 index 93df970dd6..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/prettier.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@spotify/web-scripts/config/prettier.config.js'); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/public/static/fonts.css b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/public/static/fonts.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx deleted file mode 100644 index 8612ead08f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/__tests__/index.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { cleanup, render } from '@testing-library/react'; - -import Index from '../pages/index'; - -afterEach(cleanup); - -describe('Index', () => { - it('Says hello', () => { - const { queryByText } = render(); - expect(queryByText('Hello!')).toBeTruthy(); - }); -}); diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx deleted file mode 100644 index f8ba94310a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/components/Header.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export const Header = () =>

Header

; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx deleted file mode 100644 index a3853e47c9..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_app.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import App from 'next/app'; -import Head from 'next/head'; -import styled from 'styled-components'; -import { Header } from '../components/Header'; - -const StyledApp = styled.div` - > * { - padding-left: 16px; - padding-right: 16px; - } -`; - -const Main = styled.div` - margin: 2em auto; - height: 85vh; -`; - -class CustomApp extends App { - render() { - const { Component, pageProps } = this.props; - - return ( - - - - -
-
- -
- - ); - } -} - -export default CustomApp; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx deleted file mode 100644 index 9b01a6a74c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/_document.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file extends the and injects the server side rendered styles into the - * By server-side rendering CSS we avoid visual changes in the layout while loading the JS. - * - * Taken from this example: - * https://github.com/zeit/next.js/tree/master/examples/with-styled-components - */ - -import React from 'react'; -import Document, { DocumentContext, DocumentInitialProps } from 'next/document'; -import { ServerStyleSheet } from 'styled-components'; - -export default class MyDocument extends Document { - static async getInitialProps( - ctx: DocumentContext, - ): Promise { - const sheet = new ServerStyleSheet(); - const originalRenderPage = ctx.renderPage; - - try { - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App: any) => (props) => - sheet.collectStyles(), - }); - - const initialProps = await Document.getInitialProps(ctx); - return { - ...initialProps, - styles: ( - <> - {initialProps.styles} - {sheet.getStyleElement()} - - ), - }; - } finally { - sheet.seal(); - } - } -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts deleted file mode 100644 index 8bc772c182..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/api/ping.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -export default function handle(_: NextApiRequest, res: NextApiResponse) { - res.status(200).send('ok'); -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx deleted file mode 100644 index c01d844f9f..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/src/pages/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -const Index = () =>

Hello!

; - -export default Index; diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json deleted file mode 100644 index 03508ff043..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/skeleton/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "@spotify/tsconfig", - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve" - }, - "exclude": ["node_modules", "output/node_modules"], - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml deleted file mode 100644 index 49802a9d62..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: react-ssr-template - title: React SSR Template - description: Create a website powered with Next.js - tags: - - recommended - - react -spec: - owner: web@example.com - type: website - parameters: - - title: Provide some simple information - required: - - component_id - - owner - properties: - component_id: - title: Name - type: string - description: Unique name of the component - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - owner: '{{ parameters.owner }}' - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig deleted file mode 100644 index 03eb226ef0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -continuation_indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml deleted file mode 100644 index 04e1d10b47..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.github/workflows/build.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Java CI with Maven - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Build with Maven - run: mvn -B package --file pom.xml diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore deleted file mode 100644 index 1504daa184..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -target/ -## IntelliJ -.idea -*.iml - -## Temp -dependency-reduced-pom.xml diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile deleted file mode 100644 index 1c8a969c8c..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM openjdk:11-alpine -ENTRYPOINT ["/usr/bin/{{cookiecutter.artifact_id}}.sh"] - -COPY {{cookiecutter.artifact_id}}.sh /usr/bin/{{cookiecutter.artifact_id}}.sh -COPY target/{{cookiecutter.artifact_id}}.jar /usr/share/{{cookiecutter.artifact_id}}/{{cookiecutter.artifact_id}}.jar diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md deleted file mode 100644 index 710b6b557e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# {{cookiecutter.component_id}} - -{{cookiecutter.description}} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker deleted file mode 100644 index 1dcf39a3b3..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/SERVICE.marker +++ /dev/null @@ -1 +0,0 @@ -(This marker file causes Maven to build this artifact as a service) diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml deleted file mode 100644 index 4ddec9c032..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.component_id | jsonify}} - description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} - backstage.io/techdocs-ref: dir:. -spec: - type: service - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md deleted file mode 100644 index 5352ef7801..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/docs/index.md +++ /dev/null @@ -1,28 +0,0 @@ -## {{ 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/springboot-grpc-template/skeleton/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml deleted file mode 100644 index 64f1e9ee8e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: {{cookiecutter.component_id | jsonify}} -site_description: {{cookiecutter.description | jsonify}} - -nav: - - Introduction: index.md - -plugins: - - techdocs-core diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml deleted file mode 100644 index d5e81cf5e0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/pom.xml +++ /dev/null @@ -1,221 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 2.2.4.RELEASE - - - com.example - {{cookiecutter.artifact_id}} - 0.0.1-SNAPSHOT - jar - - {{cookiecutter.artifact_id}} - {{cookiecutter.description}} - - - 11 - ${java.version} - ${java.version} - ${java.version} - UTF-8 - UTF-8 - - - - jcenter - https://jcenter.bintray.com/ - - - - - io.github.lognet - grpc-spring-boot-starter - 3.5.1 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.apache.tomcat - annotations-api - 6.0.53 - provided - - - - - - ${project.artifactId} - - - kr.motd.maven - os-maven-plugin - 1.4.0.Final - - - - - - maven-compiler-plugin - 3.8.1 - - true - true - - -Xlint:all - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - - ${project.basedir}/src/main/resources - - com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} - - grpc-java - - io.grpc:protoc-gen-grpc-java:1.13.1:exe:${os.detected.classifier} - - - - - - compile - compile-custom - - - - - - - - maven-failsafe-plugin - 2.22.2 - - - **/*IT.java - **/integrationtests/*.java - **/it/**/*.java - - - - - - - com.coveo - fmt-maven-plugin - 2.8 - - - - format - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.5 - - - - prepare-agent - report - - - - - - maven-enforcer-plugin - - - enforce - - enforce - - - - - - - - - - - - maven-jar-plugin - - - true - - ${start-class} - true - true - - - - ${project.version}-${buildTimestamp}-${buildNumber} - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - build-info - - build-info - - - - - - maven-source-plugin - - - attach-sources - - jar - - - - - - maven-javadoc-plugin - - private - - - - attach-javadocs - - jar - - - - - - - diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java deleted file mode 100644 index 7ead2562d0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/Application.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java deleted file mode 100644 index 90b8412a04..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/java/com/example/{{cookiecutter.java_package_name}}/GreeterService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import com.example.{{cookiecutter.java_package_name}}.proto.GreeterGrpc; -import com.example.{{cookiecutter.java_package_name}}.proto.GreeterOuterClass; -import io.grpc.stub.StreamObserver; -import org.lognet.springboot.grpc.GRpcService; - -@GRpcService -public class GreeterService extends GreeterGrpc.GreeterImplBase { - - @Override - public void sayHello(final GreeterOuterClass.HelloRequest request, - final StreamObserver responseObserver) { - final GreeterOuterClass.HelloReply.Builder replyBuilder = - GreeterOuterClass.HelloReply.newBuilder().setMessage("Hello " + request.getName()); - responseObserver.onNext(replyBuilder.build()); - responseObserver.onCompleted(); - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties deleted file mode 100644 index 03be3b6ebf..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/application.properties +++ /dev/null @@ -1,3 +0,0 @@ -spring.application.name={{cookiecutter.component_id}} -server.port={{cookiecutter.http_port}} -grpc.enableReflection=true diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt deleted file mode 100644 index 46846ba35e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/banner.txt +++ /dev/null @@ -1,22 +0,0 @@ -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_GREEN}8${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} -${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} ${AnsiColor.BRIGHT_WHITE} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto deleted file mode 100644 index 8015d914ee..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/main/resources/greeter.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.example.{{cookiecutter.java_package_name}}.proto"; - -service Greeter { - rpc SayHello ( HelloRequest) returns ( HelloReply) {} -} - -// The request message containing the user's name. -message HelloRequest { - string name = 1; -} - -// The response message containing the greetings -message HelloReply { - string message = 1; -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java deleted file mode 100644 index 1d7298172a..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/src/test/java/com/example/{{cookiecutter.java_package_name}}/ApplicationTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.{{cookiecutter.java_package_name}}; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class ApplicationTest { - - @Test - public void contextLoads() { - } -} diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh deleted file mode 100755 index c69b980dc8..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/skeleton/{{cookiecutter.artifact_id}}.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec /usr/bin/java $JVM_DEFAULT_ARGS $JVM_ARGS -jar /usr/share/{{cookiecutter.artifact_id}}/{{cookiecutter.artifact_id}}.jar "$@" diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml deleted file mode 100644 index c306187581..0000000000 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml +++ /dev/null @@ -1,88 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: springboot-template - title: Spring Boot gRPC Service - description: Create a simple microservice using gRPC and Spring Boot Java - tags: - - recommended - - java - - grpc -spec: - owner: service@example.com - type: service - - parameters: - - title: Provide some simple information - required: - - component_id - - owner - - java_package_name - properties: - component_id: - title: Name - type: string - description: Unique name of the component - java_package_name: - title: Java Package Name - type: string - description: Name for the java package. eg (io.backstage.blah) - description: - title: Description - type: string - description: Help others understand what this website is for. - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - - steps: - - id: template - name: Fetch Skeleton + Template - action: fetch:cookiecutter - input: - url: ./skeleton - copyWithoutRender: - - .github/workflows/* - values: - component_id: '{{ parameters.component_id }}' - description: '{{ parameters.description }}' - artifact_id: '{{ parameters.component_id }}' - java_package_name: '{{ parameters.java_package_name }}' - owner: '{{ parameters.owner }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - http_port: 8080 - - - id: publish - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.component_id }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - output: - remoteUrl: '{{ steps.publish.output.remoteUrl }}' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml deleted file mode 100644 index b3228a20d0..0000000000 --- a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: backstage.io/v1beta2 -kind: Template -metadata: - name: v1beta2-demo - title: Test Action template - description: scaffolder v1beta2 template demo publishing to github -spec: - owner: backstage/techdocs-core - type: service - - parameters: - - title: Fill in some steps - required: - - name - - owner - properties: - name: - title: Name - type: string - description: Unique name of the component - ui:autofocus: true - ui:options: - rows: 5 - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - system: - title: System - type: string - description: System of the component - ui:field: EntityPicker - ui:options: - allowedKinds: - - System - defaultKind: System - - - title: Choose a location - required: - - repoUrl - - dryRun - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com - dryRun: - title: Only perform a dry run, don't publish anything - type: boolean - default: false - - steps: - - id: fetch-base - name: Fetch Base - action: fetch:cookiecutter - input: - url: ./template - values: - name: '{{ parameters.name }}' - owner: '{{ parameters.owner }}' - system: '{{ parameters.system }}' - destination: '{{ parseRepoUrl parameters.repoUrl }}' - - - id: fetch-docs - name: Fetch Docs - action: fetch:plain - input: - targetPath: ./community - url: https://github.com/backstage/community/tree/main/backstage-community-sessions - - - id: publish - if: '{{ not parameters.dryRun }}' - name: Publish - action: publish:github - input: - allowedHosts: ['github.com'] - description: 'This is {{ parameters.name }}' - repoUrl: '{{ parameters.repoUrl }}' - - - id: register - if: '{{ not parameters.dryRun }}' - name: Register - action: catalog:register - input: - repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' - catalogInfoPath: '/catalog-info.yaml' - - - name: Results - if: '{{ parameters.dryRun }}' - action: debug:log - input: - listWorkspace: true - - output: - links: - - title: Repository - url: '{{ steps.publish.output.remoteUrl }}' - - title: Open in catalog - icon: 'catalog' - entityRef: '{{ steps.register.output.entityRef }}' diff --git a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml deleted file mode 100644 index 02da577b39..0000000000 --- a/plugins/scaffolder-backend/sample-templates/v1beta2-demo/template/catalog-info.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: {{cookiecutter.name | jsonify}} - github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}} -spec: - type: website - lifecycle: experimental - owner: {{cookiecutter.owner | jsonify}} -{%- if 'system' in cookiecutter %} - system: {{ cookiecutter.system | jsonify }} -{%- endif %} From 618143c3c78c8cdd9a3e1b19c9ed7681b21b98b2 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 12 Aug 2021 08:07:55 +0200 Subject: [PATCH 004/250] scaffolder-backend: changeset about removing sample-templates Signed-off-by: Himanshu Mishra --- .changeset/cuddly-rabbits-look.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cuddly-rabbits-look.md diff --git a/.changeset/cuddly-rabbits-look.md b/.changeset/cuddly-rabbits-look.md new file mode 100644 index 0000000000..421c97bbff --- /dev/null +++ b/.changeset/cuddly-rabbits-look.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Action needed: If you are using the templates located at https://github.com/backstage/backstage/tree/master/ in your Backstage app directly using the URL via the `app-config.yaml`, you should copy over the templates inside your org and import from there. The templates have now been moved to https://github.com/backstage/software-templates. See https://github.com/backstage/backstage/issues/6415 for explanation. From 4d58d1ebf682c005c61fc1a6b25cc1d4dae8b87e Mon Sep 17 00:00:00 2001 From: victormorfin97 Date: Fri, 13 Aug 2021 12:19:18 -0500 Subject: [PATCH 005/250] Add props to support the custom templateCard component Signed-off-by: victormorfin97 --- plugins/scaffolder/src/components/Router.tsx | 11 ++++++++-- .../ScaffolderPage/ScaffolderPage.tsx | 14 ++++++++---- .../components/TemplateList/TemplateList.tsx | 22 +++++++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 3d7bbcaf5f..3d7badc6e7 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -29,7 +29,11 @@ import { } from '../extensions'; import { useElementFilter } from '@backstage/core-plugin-api'; -export const Router = () => { +type RouterProps = { + renderTemplateCard?: Function | undefined; +}; + +export const Router = ({ renderTemplateCard }: RouterProps) => { const outlet = useOutlet(); const customFieldExtensions = useElementFilter(outlet, elements => @@ -54,7 +58,10 @@ export const Router = () => { return ( - } /> + } + /> } diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 859e5bba93..a9b8c1b573 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -46,7 +46,13 @@ const useStyles = makeStyles(theme => ({ }, })); -export const ScaffolderPageContents = () => { +export type ScaffolderPageProps = { + renderTemplateCard: Function | undefined; +}; + +export const ScaffolderPageContents = ({ + renderTemplateCard, +}: ScaffolderPageProps) => { const styles = useStyles(); const registerComponentLink = useRouteRef(registerComponentRouteRef); @@ -87,7 +93,7 @@ export const ScaffolderPageContents = () => {
- +
@@ -95,8 +101,8 @@ export const ScaffolderPageContents = () => { ); }; -export const ScaffolderPage = () => ( +export const ScaffolderPage = ({ renderTemplateCard }: ScaffolderPageProps) => ( - + ); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx index 009342c379..8fe56d4a5f 100644 --- a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -25,7 +25,11 @@ import { useEntityListProvider } from '@backstage/plugin-catalog-react'; import { Link, Typography } from '@material-ui/core'; import { TemplateCard } from '../TemplateCard'; -export const TemplateList = () => { +export type TemplateListProps = { + renderTemplateCard?: Function | undefined; +}; + +export const TemplateList = ({ renderTemplateCard }: TemplateListProps) => { const { loading, error, entities } = useEntityListProvider(); return ( <> @@ -50,12 +54,16 @@ export const TemplateList = () => { {entities && entities?.length > 0 && - entities.map((template, i) => ( - - ))} + entities.map((template, i) => + renderTemplateCard ? ( + renderTemplateCard(i, template) + ) : ( + + ), + )} ); From 3f4830ff649f0a1d90dfd3383facac61e75aa633 Mon Sep 17 00:00:00 2001 From: victormorfin97 Date: Fri, 13 Aug 2021 13:35:28 -0500 Subject: [PATCH 006/250] export things to replicate the templateCard Signed-off-by: victormorfin97 --- .../src/components/FavouriteTemplate/index.ts | 17 +++++++++++++++++ plugins/scaffolder/src/index.ts | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 plugins/scaffolder/src/components/FavouriteTemplate/index.ts diff --git a/plugins/scaffolder/src/components/FavouriteTemplate/index.ts b/plugins/scaffolder/src/components/FavouriteTemplate/index.ts new file mode 100644 index 0000000000..5955e88c01 --- /dev/null +++ b/plugins/scaffolder/src/components/FavouriteTemplate/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './FavouriteTemplate'; diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index 989b3b53ce..8a7dea1c34 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -37,3 +37,5 @@ export { RepoUrlPicker, TextValuePicker, } from './components/fields'; +export { FavouriteTemplate } from './components/FavouriteTemplate'; +export { rootRouteRef as scaffolderRootRouteRef } from './routes'; From 34e14fdf7e59b87db3ec08b1b5a6d9767a735986 Mon Sep 17 00:00:00 2001 From: victormorfin97 Date: Fri, 13 Aug 2021 13:53:17 -0500 Subject: [PATCH 007/250] Add changeset Signed-off-by: victormorfin97 --- .changeset/beige-glasses-dream.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/beige-glasses-dream.md diff --git a/.changeset/beige-glasses-dream.md b/.changeset/beige-glasses-dream.md new file mode 100644 index 0000000000..c1a9ad1857 --- /dev/null +++ b/.changeset/beige-glasses-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Allow to pass custom TemplateCard to ScaffolderPage From c2d0bd6902a6b4c1fa364c728a3c84400ee52e5b Mon Sep 17 00:00:00 2001 From: victormorfin97 Date: Fri, 13 Aug 2021 16:16:56 -0500 Subject: [PATCH 008/250] renaming prop Signed-off-by: victormorfin97 --- plugins/scaffolder/src/components/Router.tsx | 6 +++--- .../src/components/ScaffolderPage/ScaffolderPage.tsx | 10 +++++----- .../src/components/TemplateList/TemplateList.tsx | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 3d7badc6e7..e317612d33 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -30,10 +30,10 @@ import { import { useElementFilter } from '@backstage/core-plugin-api'; type RouterProps = { - renderTemplateCard?: Function | undefined; + templateCard?: Function | undefined; }; -export const Router = ({ renderTemplateCard }: RouterProps) => { +export const Router = ({ templateCard }: RouterProps) => { const outlet = useOutlet(); const customFieldExtensions = useElementFilter(outlet, elements => @@ -60,7 +60,7 @@ export const Router = ({ renderTemplateCard }: RouterProps) => { } + element={} /> ({ })); export type ScaffolderPageProps = { - renderTemplateCard: Function | undefined; + templateCard: Function | undefined; }; export const ScaffolderPageContents = ({ - renderTemplateCard, + templateCard, }: ScaffolderPageProps) => { const styles = useStyles(); @@ -93,7 +93,7 @@ export const ScaffolderPageContents = ({
- +
@@ -101,8 +101,8 @@ export const ScaffolderPageContents = ({ ); }; -export const ScaffolderPage = ({ renderTemplateCard }: ScaffolderPageProps) => ( +export const ScaffolderPage = ({ templateCard }: ScaffolderPageProps) => ( - + ); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx index 8fe56d4a5f..3e13b82b2b 100644 --- a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -26,10 +26,10 @@ import { Link, Typography } from '@material-ui/core'; import { TemplateCard } from '../TemplateCard'; export type TemplateListProps = { - renderTemplateCard?: Function | undefined; + templateCard?: Function | undefined; }; -export const TemplateList = ({ renderTemplateCard }: TemplateListProps) => { +export const TemplateList = ({ templateCard }: TemplateListProps) => { const { loading, error, entities } = useEntityListProvider(); return ( <> @@ -55,8 +55,8 @@ export const TemplateList = ({ renderTemplateCard }: TemplateListProps) => { {entities && entities?.length > 0 && entities.map((template, i) => - renderTemplateCard ? ( - renderTemplateCard(i, template) + templateCard ? ( + templateCard(i, template) ) : ( Date: Wed, 18 Aug 2021 18:22:37 -0300 Subject: [PATCH 009/250] added title Signed-off-by: Aurelio Saraiva --- .../examples/documented-component/catalog-info.yaml | 1 + plugins/techdocs/src/home/components/columns.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml index 125c18200d..e6d14025e1 100644 --- a/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml +++ b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml @@ -2,6 +2,7 @@ apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: documented-component + title: Documented Component description: A Service with TechDocs documentation annotations: backstage.io/techdocs-ref: dir:. diff --git a/plugins/techdocs/src/home/components/columns.tsx b/plugins/techdocs/src/home/components/columns.tsx index 5cdcc66c37..80dfd80a35 100644 --- a/plugins/techdocs/src/home/components/columns.tsx +++ b/plugins/techdocs/src/home/components/columns.tsx @@ -19,6 +19,10 @@ import { Link, SubvalueCell, TableColumn } from '@backstage/core-components'; import { EntityRefLinks } from '@backstage/plugin-catalog-react'; import { DocsTableRow } from './types'; +function formatTitle(entity): String { + return entity.metadata.title || entity.metadata.name; +} + export function createNameColumn(): TableColumn { return { title: 'Document', @@ -26,9 +30,7 @@ export function createNameColumn(): TableColumn { highlight: true, render: (row: DocsTableRow) => ( {row.entity.metadata.name} - } + value={{formatTitle(row.entity)}} subvalue={row.entity.metadata.description} /> ), From 60169a761d5c98821e4f1bf6488a3c2e30188778 Mon Sep 17 00:00:00 2001 From: Aurelio Saraiva Date: Wed, 18 Aug 2021 19:34:16 -0300 Subject: [PATCH 010/250] renamed function to customTitle Signed-off-by: Aurelio Saraiva --- plugins/techdocs/src/home/components/columns.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/home/components/columns.tsx b/plugins/techdocs/src/home/components/columns.tsx index 80dfd80a35..b7ec8271a4 100644 --- a/plugins/techdocs/src/home/components/columns.tsx +++ b/plugins/techdocs/src/home/components/columns.tsx @@ -17,9 +17,10 @@ import React from 'react'; import { Link, SubvalueCell, TableColumn } from '@backstage/core-components'; import { EntityRefLinks } from '@backstage/plugin-catalog-react'; +import { Entity } from '@backstage/catalog-model'; import { DocsTableRow } from './types'; -function formatTitle(entity): String { +function customTitle(entity: Entity): String { return entity.metadata.title || entity.metadata.name; } @@ -30,7 +31,7 @@ export function createNameColumn(): TableColumn { highlight: true, render: (row: DocsTableRow) => ( {formatTitle(row.entity)}} + value={{customTitle(row.entity)}} subvalue={row.entity.metadata.description} /> ), From 4578d0708c528a22eed08d759113dcfb0bc6eb3e Mon Sep 17 00:00:00 2001 From: Aurelio Saraiva Date: Wed, 18 Aug 2021 19:49:37 -0300 Subject: [PATCH 011/250] updated docs with new property title Signed-off-by: Aurelio Saraiva --- docs/features/software-catalog/descriptor-format.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index fb17f4a434..f39454a797 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -231,6 +231,14 @@ follows. Example: `visits-tracking-service`, `CircleciBuildsDumpV2_avro_gcs` +### `title` [optional] + +The title of the entity. This is a friendly title to be used in the Backstage +interface. This is an optional property, if it doesn't exist the `name` property +will be used. + +Only available for Templates and Docs. + ### `namespace` [optional] The ID of a namespace that the entity belongs to. This is a string that follows From 13dc3735cc345783cac068027a49b23a048daf86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 19 Aug 2021 14:52:05 +0200 Subject: [PATCH 012/250] Add the metadata.title field to all kinds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/strange-zoos-grab.md | 11 ++++++++ .../software-catalog/descriptor-format.md | 22 +++++++++++----- packages/catalog-model/api-report.md | 5 +--- packages/catalog-model/src/entity/Entity.ts | 25 ++++++++++++++++++- .../src/kinds/TemplateEntityV1beta2.ts | 5 +--- .../src/schema/EntityMeta.schema.json | 6 +++++ .../schema/kinds/Template.v1beta2.schema.json | 11 -------- .../validation/entitySchemaValidator.test.ts | 17 +++++++++++++ 8 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 .changeset/strange-zoos-grab.md diff --git a/.changeset/strange-zoos-grab.md b/.changeset/strange-zoos-grab.md new file mode 100644 index 0000000000..78a6c6eeee --- /dev/null +++ b/.changeset/strange-zoos-grab.md @@ -0,0 +1,11 @@ +--- +'@backstage/catalog-model': patch +--- + +Add an optional `metadata.title` field to all entity kinds. + +This used to be available on only the `Template` kind, and we have decided that the metadata block should be the same for all kinds. A title can be useful especially in large and complex catalogs where users have a tough time navigating or discerning among the entities. + +It also carries some risk. You do not want to end up giving a title that collides with an actual name, which at best leads to confusion and at worst could be a liability. We do not perform any collision detection in the catalog. If you want to disallow this facility you may want to add a small processor that makes sure it's not set. + +At the time of writing this message, only the scaffolder actually makes use of this field for display purposes. diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index fb17f4a434..1aadbbf981 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -245,6 +245,22 @@ the entity belongs to the `"default"` namespace. Namespaces may also be part of the catalog, and are `v1` / `Namespace` entities, i.e. not Backstage specific but the same as in Kubernetes. +### `title` [optional] + +A nice display name of the entity, to be presented in user interfaces instead of +the `name` property above, when available. + +This field is sometimes useful when the `name` is cumbersome or ends up being +perceived as overly technical. The title generally does not have as stringent +format requirements on it, so it may contain special characters and be more +explanatory. Do keep it very short though, and avoid situations where a title +can be confused with the name of another entity, or where two entities share a +title. + +Note that this is only for display purposes, and may be ignored by some parts of +the code. [Entity references](references.md) still always make use of the `name` +property for example, not the title. + ### `description` [optional] A human readable description of the entity, to be shown in Backstage. Should be @@ -690,12 +706,6 @@ shape, this kind has the following structure. Exactly equal to `backstage.io/v1beta2` and `Template`, respectively. -### `metadata.title` [required] - -The nice display name for the template as a string, e.g. `React SSR Template`. -This field is required as is used to reference the template to the user instead -of the `metadata.name` field. - ### `metadata.tags` [optional] A list of strings that can be associated with the template, e.g. diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 13041d9e32..b9f46add6a 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -230,6 +230,7 @@ export type EntityMeta = JsonObject & { generation?: number; name: string; namespace?: string; + title?: string; description?: string; labels?: Record; annotations?: Record; @@ -722,10 +723,6 @@ export interface TemplateEntityV1beta2 extends Entity { // (undocumented) kind: 'Template'; // (undocumented) - metadata: EntityMeta & { - title?: string; - }; - // (undocumented) spec: { type: string; parameters?: JsonObject | JsonObject[]; diff --git a/packages/catalog-model/src/entity/Entity.ts b/packages/catalog-model/src/entity/Entity.ts index 8843f10f5f..5b20d72c6f 100644 --- a/packages/catalog-model/src/entity/Entity.ts +++ b/packages/catalog-model/src/entity/Entity.ts @@ -103,7 +103,13 @@ export type EntityMeta = JsonObject & { * The name of the entity. * * Must be unique within the catalog at any given point in time, for any - * given namespace + kind pair. + * given namespace + kind pair. This value is part of the technical + * identifier of the entity, and as such it will appear in URLs, database + * tables, entity references, and similar. It is subject to restrictions + * regarding what characters are allowed. + * + * If you want to use a different, more human readable string with fewer + * restrictions on it in user interfaces, see the `title` field below. */ name: string; @@ -112,6 +118,23 @@ export type EntityMeta = JsonObject & { */ namespace?: string; + /** + * A nice display name of the entity, to be presented in user interfaces + * instead of the `name` property above, when available. + * + * This field is sometimes useful when the `name` is cumbersome or ends up + * being perceived as overly technical. The title generally does not have + * as stringent format requirements on it, so it may contain special + * characters and be more explanatory. Do keep it very short though, and + * avoid situations where a title can be confused with the name of another + * entity, or where two entities share a title. + * + * Note that this is only for display purposes, and may be ignored by some + * parts of the code. Entity references still always make use of the `name` + * property, not the title. + */ + title?: string; + /** * A short (typically relatively few words, on one line) description of the * entity. diff --git a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts index 577e5ec9e2..1d7aa2162c 100644 --- a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts +++ b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts @@ -15,16 +15,13 @@ */ import { JsonObject } from '@backstage/config'; -import type { Entity, EntityMeta } from '../entity/Entity'; +import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Template.v1beta2.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; export interface TemplateEntityV1beta2 extends Entity { apiVersion: 'backstage.io/v1beta2'; kind: 'Template'; - metadata: EntityMeta & { - title?: string; - }; spec: { type: string; parameters?: JsonObject | JsonObject[]; diff --git a/packages/catalog-model/src/schema/EntityMeta.schema.json b/packages/catalog-model/src/schema/EntityMeta.schema.json index ff0f9c84a8..105b698940 100644 --- a/packages/catalog-model/src/schema/EntityMeta.schema.json +++ b/packages/catalog-model/src/schema/EntityMeta.schema.json @@ -53,6 +53,12 @@ "examples": ["default", "admin"], "minLength": 1 }, + "title": { + "type": "string", + "description": "A nice display name of the entity, to be presented in user interfaces instead of the name property, when available.", + "examples": ["React SSR Template"], + "minLength": 1 + }, "description": { "type": "string", "description": "A short (typically relatively few words, on one line) description of the entity." diff --git a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json index b14cdb8ee0..d68eac936d 100644 --- a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json +++ b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json @@ -69,17 +69,6 @@ "kind": { "enum": ["Template"] }, - "metadata": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The nice display name for the template.", - "examples": ["React SSR Template"], - "minLength": 1 - } - } - }, "spec": { "type": "object", "required": ["type", "steps"], diff --git a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts index 3dfe307f92..b486de1df2 100644 --- a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts +++ b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts @@ -30,6 +30,8 @@ describe('entitySchemaValidator', () => { generation: 13, name: 'test', namespace: 'ns', + title: 'My Component, Yay', + description: 'Yeah this is probably the best component so far', labels: { 'backstage.io/custom': 'ValueStuff', }, @@ -192,6 +194,21 @@ describe('entitySchemaValidator', () => { expect(() => validator(entity)).toThrow(/namespace/); }); + it('accepts missing title', () => { + delete entity.metadata.title; + expect(() => validator(entity)).not.toThrow(); + }); + + it('rejects bad title type', () => { + entity.metadata.title = 7; + expect(() => validator(entity)).toThrow(/title/); + }); + + it('rejects empty title', () => { + entity.metadata.title = ''; + expect(() => validator(entity)).toThrow(/title/); + }); + it('accepts missing description', () => { delete entity.metadata.description; expect(() => validator(entity)).not.toThrow(); From 9388571643faa5d074b49133dce413dca91137ea Mon Sep 17 00:00:00 2001 From: victormorfin97 Date: Thu, 19 Aug 2021 11:19:24 -0500 Subject: [PATCH 013/250] Change name & form of the new prop Signed-off-by: victormorfin97 --- plugins/scaffolder/src/components/Router.tsx | 8 +++++--- .../components/ScaffolderPage/ScaffolderPage.tsx | 12 +++++++----- .../src/components/TemplateList/TemplateList.tsx | 15 ++++++++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index e317612d33..6a08d46ec9 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -30,10 +30,10 @@ import { import { useElementFilter } from '@backstage/core-plugin-api'; type RouterProps = { - templateCard?: Function | undefined; + TemplateCardComponent?: (props: any) => JSX.Element | null; }; -export const Router = ({ templateCard }: RouterProps) => { +export const Router = ({ TemplateCardComponent }: RouterProps) => { const outlet = useOutlet(); const customFieldExtensions = useElementFilter(outlet, elements => @@ -60,7 +60,9 @@ export const Router = ({ templateCard }: RouterProps) => { } + element={ + + } /> ({ })); export type ScaffolderPageProps = { - templateCard: Function | undefined; + TemplateCardComponent?: (props: any) => JSX.Element | null; }; export const ScaffolderPageContents = ({ - templateCard, + TemplateCardComponent, }: ScaffolderPageProps) => { const styles = useStyles(); @@ -93,7 +93,7 @@ export const ScaffolderPageContents = ({
- +
@@ -101,8 +101,10 @@ export const ScaffolderPageContents = ({ ); }; -export const ScaffolderPage = ({ templateCard }: ScaffolderPageProps) => ( +export const ScaffolderPage = ({ + TemplateCardComponent, +}: ScaffolderPageProps) => ( - + ); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx index 3e13b82b2b..b90ed1d8a6 100644 --- a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -23,13 +23,15 @@ import { } from '@backstage/core-components'; import { useEntityListProvider } from '@backstage/plugin-catalog-react'; import { Link, Typography } from '@material-ui/core'; -import { TemplateCard } from '../TemplateCard'; +import { TemplateCard, TemplateCardProps } from '../TemplateCard'; + +export type Props = TemplateCardProps; export type TemplateListProps = { - templateCard?: Function | undefined; + TemplateCardComponent?: (props: Props) => JSX.Element | null; }; -export const TemplateList = ({ templateCard }: TemplateListProps) => { +export const TemplateList = ({ TemplateCardComponent }: TemplateListProps) => { const { loading, error, entities } = useEntityListProvider(); return ( <> @@ -55,8 +57,11 @@ export const TemplateList = ({ templateCard }: TemplateListProps) => { {entities && entities?.length > 0 && entities.map((template, i) => - templateCard ? ( - templateCard(i, template) + TemplateCardComponent ? ( + ) : ( Date: Mon, 23 Aug 2021 04:08:14 +0000 Subject: [PATCH 014/250] chore(deps): bump @rjsf/material-ui from 3.0.0 to 3.1.0 Bumps [@rjsf/material-ui](https://github.com/rjsf-team/react-jsonschema-form) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/rjsf-team/react-jsonschema-form/releases) - [Commits](https://github.com/rjsf-team/react-jsonschema-form/compare/v3.0.0...v3.1.0) --- updated-dependencies: - dependency-name: "@rjsf/material-ui" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 94b81f35ee..59deeb3b96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5053,9 +5053,9 @@ react-is "^16.9.0" "@rjsf/material-ui@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-3.0.0.tgz#69ece2cb549f0e860b5f89898db90edcc95b15ba" - integrity sha512-T2B8QnrDQphbFNxDz7baAa0zTd5TXJmO9soHBPTKKdniRbMEOQ19AJBbZkA3ED2XZa/xrUY/6XjERQLpNACddw== + version "3.1.0" + resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-3.1.0.tgz#ed15fc75de1594f87cd8336b7f2ebc492a432d8b" + integrity sha512-kWz37spT5SOXkb8Axq4g4BzQjXRylQr6B7eFAg1NPhSK7KrJYwSMSsFJ9Ze2vEBxwEiR0Z0n/4puaamGuGFRBw== "@roadiehq/backstage-plugin-buildkite@^1.0.8": version "1.0.8" From 3f9dd175989c87693aba596f30c4ae1f6472ec5b Mon Sep 17 00:00:00 2001 From: "@pawelmitka" Date: Mon, 23 Aug 2021 13:23:49 +0200 Subject: [PATCH 015/250] feat(scaffolder-plugin-backend): GitHub create repository webhook `github:repository:webhook:create` action has been added for GitHub. This allows creating custom webhooks, e.g.: for CI integration. Configuration has been simplified to the usage of secrets. Therefore, `config.token` and `config.digest` has been ommitted in favor of Secrets usage as through GitHub GUI. Secrets are assumed to be passed through environment variable name. Reference for `octokit.rest.repos.createWebhook` - https://octokit.github.io/rest.js/v18. All the defaults are aligned with the Octokit defaults. Note: there is some duplicated code related to GitHub integration that is shared between `createGithubCreateRepositoryWebhookAction`, `createGithubActionsDispatchAction` and `createPublishGithubAction` that I intend to refactor in a separate PR afterwards. Signed-off-by: @pawelmitka --- .changeset/fair-files-rest.md | 5 + .../__mocks__/@octokit/rest/index.ts | 1 + .../actions/builtin/createBuiltinActions.ts | 8 +- .../githubCreateRepositoryWebhook.test.ts | 228 ++++++++++++++++++ .../github/githubCreateRepositoryWebhook.ts | 176 ++++++++++++++ .../actions/builtin/github/index.ts | 1 + 6 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 .changeset/fair-files-rest.md create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.ts diff --git a/.changeset/fair-files-rest.md b/.changeset/fair-files-rest.md new file mode 100644 index 0000000000..3a16bb98ef --- /dev/null +++ b/.changeset/fair-files-rest.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +GitHub create repository webhook action: 'github:repository:webhook:create' for Backstage plugin Scaffolder has been added. diff --git a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts index 7505c3a30e..b60c3fcb2d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts @@ -23,6 +23,7 @@ export const mockGithubClient = { repos: { createInOrg: jest.fn(), createForAuthenticatedUser: jest.fn(), + createWebhook: jest.fn(), addCollaborator: jest.fn(), replaceAllTopics: jest.fn(), }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 5a16ef9a55..698389b7a7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -37,7 +37,10 @@ import { createPublishGithubPullRequestAction, createPublishGitlabAction, } from './publish'; -import { createGithubActionsDispatchAction } from './github'; +import { + createGithubActionsDispatchAction, + createGithubCreateRepositoryWebhookAction, +} from './github'; export const createBuiltinActions = (options: { reader: UrlReader; @@ -90,5 +93,8 @@ export const createBuiltinActions = (options: { createGithubActionsDispatchAction({ integrations, }), + createGithubCreateRepositoryWebhookAction({ + integrations, + }), ]; }; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.test.ts new file mode 100644 index 0000000000..166ca15272 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.test.ts @@ -0,0 +1,228 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +jest.mock('@octokit/rest'); + +import { createGithubCreateRepositoryWebhookAction } from './githubCreateRepositoryWebhook'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; + +describe('github:repository:webhook:create', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createGithubCreateRepositoryWebhookAction({ integrations }); + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + webhookUrl: 'https://example.com/payload', + webhookSecretEnv: 'SUPER_SECRET', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + const { mockGithubClient } = require('@octokit/rest'); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'github.com?repo=bob' }, + }), + ).rejects.toThrow(/missing owner/); + + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'github.com?owner=owner' }, + }), + ).rejects.toThrow(/missing repo/); + }); + + it('should throw if there is no integration config provided', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'missing.com?repo=bob&owner=owner' }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + it('should throw if there is no token in the integration config that is returned', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + repoUrl: 'ghe.github.com?repo=bob&owner=owner', + }, + }), + ).rejects.toThrow(/No token available for host/); + }); + + it('should throw if environment variable in secret is invalid', async () => { + await expect(action.handler(mockContext)).rejects.toThrow( + /Environment variable: SUPER_SECRET does not exist./, + ); + }); + + it('should call the githubApi for creating repository Webhook', async () => { + const repoUrl = 'github.com?repo=repo&owner=owner'; + const webhookUrl = 'https://example.com/payload'; + const webhookSecretEnv = 'SUPER_SECRET'; + const secret = 'aafdfdivierernfdk23f'; + process.env[webhookSecretEnv] = secret; + const ctx = Object.assign({}, mockContext, { + input: { repoUrl, webhookUrl, webhookSecretEnv }, + }); + await action.handler(ctx); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push'], + active: true, + config: { + url: webhookUrl, + content_type: 'form', + secret, + insecure_ssl: '0', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + events: ['push', 'pull_request'], + }, + }); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push', 'pull_request'], + active: true, + config: { + url: webhookUrl, + content_type: 'form', + secret, + insecure_ssl: '0', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + contentType: 'json', + }, + }); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push'], + active: true, + config: { + url: webhookUrl, + content_type: 'json', + secret, + insecure_ssl: '0', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + insecureSsl: true, + }, + }); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push'], + active: true, + config: { + url: webhookUrl, + content_type: 'form', + secret, + insecure_ssl: '1', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + insecureSsl: true, + }, + }); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push'], + active: true, + config: { + url: webhookUrl, + content_type: 'form', + secret, + insecure_ssl: '1', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + active: false, + }, + }); + + expect(mockGithubClient.repos.createWebhook).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + events: ['push'], + active: false, + config: { + url: webhookUrl, + content_type: 'form', + secret, + insecure_ssl: '0', + }, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.ts new file mode 100644 index 0000000000..32ebbcae31 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubCreateRepositoryWebhook.ts @@ -0,0 +1,176 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { InputError } from '@backstage/errors'; +import { + GithubCredentialsProvider, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { Octokit } from '@octokit/rest'; +import { parseRepoUrl } from '../publish/util'; +import { createTemplateAction } from '../../createTemplateAction'; + +type ContentType = 'form' | 'json'; + +export function createGithubCreateRepositoryWebhookAction(options: { + integrations: ScmIntegrationRegistry; +}) { + const { integrations } = options; + + const credentialsProviders = new Map( + integrations.github.list().map(integration => { + const provider = GithubCredentialsProvider.create(integration.config); + return [integration.config.host, provider]; + }), + ); + + return createTemplateAction<{ + repoUrl: string; + webhookUrl: string; + webhookSecretEnv?: string; + events?: string[]; + active?: boolean; + contentType?: ContentType; + insecureSsl?: boolean; + }>({ + id: 'github:repository:webhook:create', + description: 'Creates webhook for a repository on GitHub.', + schema: { + input: { + type: 'object', + required: ['repoUrl', 'webhookUrl'], + properties: { + repoUrl: { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, + type: 'string', + }, + webhookUrl: { + title: 'Webhook URL', + description: 'The URL to which the payloads will be delivered', + type: 'string', + }, + webhookSecretEnv: { + title: 'Environment variable containing Webhook Secret', + description: 'Name of the environment variable containing secret', + type: 'string', + }, + events: { + title: 'Triggering Events', + description: + 'Determines what events the hook is triggered for. Default: push', + type: 'array', + items: { + type: 'string', + }, + }, + active: { + title: 'Active', + type: 'boolean', + description: `Determines if notifications are sent when the webhook is triggered. Default: true`, + }, + contentType: { + title: 'Content Type', + type: 'string', + enum: ['form', 'json'], + description: `The media type used to serialize the payloads. The default is 'form'`, + }, + insecureSsl: { + title: 'Insecure SSL', + type: 'boolean', + description: `Determines whether the SSL certificate of the host for url will be verified when delivering payloads. Default 'false'`, + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + webhookUrl, + webhookSecretEnv, + events = ['push'], + active = true, + contentType = 'form', + insecureSsl = false, + } = ctx.input; + + const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); + + if (!owner) { + throw new InputError(`No owner provided for repo ${repoUrl}`); + } + + let secret; + if (webhookSecretEnv) { + secret = process.env[webhookSecretEnv]; + if (!secret) { + throw new InputError( + `Environment variable: ${webhookSecretEnv} does not exist.`, + ); + } + } + + ctx.logger.info(`Creating webhook ${webhookUrl} for repo ${repoUrl}`); + + const credentialsProvider = credentialsProviders.get(host); + const integrationConfig = integrations.github.byHost(host); + + if (!credentialsProvider || !integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + const { token } = await credentialsProvider.getCredentials({ + url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent( + repo, + )}`, + }); + + if (!token) { + throw new InputError( + `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`, + ); + } + + const client = new Octokit({ + auth: token, + baseUrl: integrationConfig.config.apiBaseUrl, + previews: ['nebula-preview'], + }); + + try { + const insecure_ssl = insecureSsl ? '1' : '0'; + await client.repos.createWebhook({ + owner, + repo, + config: { + url: webhookUrl, + content_type: contentType, + secret, + insecure_ssl, + }, + events, + active, + }); + ctx.logger.info(`Webhook '${webhookUrl}' created successfully`); + } catch (e) { + ctx.logger.warn( + `Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`, + ); + } + }, + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts index 50abd2c3dc..9c12d40a65 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts @@ -15,3 +15,4 @@ */ export { createGithubActionsDispatchAction } from './githubActionsDispatch'; +export { createGithubCreateRepositoryWebhookAction } from './githubCreateRepositoryWebhook'; From 7666065a1e705b63bd270cfe42f5b03a345a153d Mon Sep 17 00:00:00 2001 From: "@pawelmitka" Date: Mon, 23 Aug 2021 14:34:00 +0200 Subject: [PATCH 016/250] fix: vale errors Signed-off-by: @pawelmitka --- .changeset/fair-files-rest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fair-files-rest.md b/.changeset/fair-files-rest.md index 3a16bb98ef..c15a4bb8cf 100644 --- a/.changeset/fair-files-rest.md +++ b/.changeset/fair-files-rest.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -GitHub create repository webhook action: 'github:repository:webhook:create' for Backstage plugin Scaffolder has been added. +GitHub create repository webhook action: `github:repository:webhook:create` for Backstage plugin Scaffolder has been added. From 4e7c25ff66f858141eb10a992102ac58bfdfe83d Mon Sep 17 00:00:00 2001 From: Deepak Bhardwaj Date: Mon, 23 Aug 2021 19:03:54 +0530 Subject: [PATCH 017/250] Created Allure Plugin Signed-off-by: Deepak Bhardwaj --- app-config.yaml | 3 + packages/app/package.json | 1 + .../app/src/components/catalog/EntityPage.tsx | 5 ++ plugins/allure/.eslintrc.js | 3 + plugins/allure/README.md | 47 +++++++++++++ plugins/allure/package.json | 68 ++++++++++++++++++ plugins/allure/src/api/AllureApi.ts | 24 +++++++ plugins/allure/src/api/AllureApiClient.ts | 30 ++++++++ plugins/allure/src/api/index.ts | 17 +++++ .../AllureReportComponent.test.tsx | 44 ++++++++++++ .../AllureReportComponent.tsx | 70 +++++++++++++++++++ .../components/AllureReportComponent/index.ts | 16 +++++ .../src/components/useAllureProjectId.ts | 25 +++++++ plugins/allure/src/index.ts | 16 +++++ plugins/allure/src/plugin.test.ts | 22 ++++++ plugins/allure/src/plugin.ts | 53 ++++++++++++++ plugins/allure/src/setupTests.ts | 17 +++++ 17 files changed, 461 insertions(+) create mode 100644 plugins/allure/.eslintrc.js create mode 100644 plugins/allure/README.md create mode 100644 plugins/allure/package.json create mode 100644 plugins/allure/src/api/AllureApi.ts create mode 100644 plugins/allure/src/api/AllureApiClient.ts create mode 100644 plugins/allure/src/api/index.ts create mode 100644 plugins/allure/src/components/AllureReportComponent/AllureReportComponent.test.tsx create mode 100644 plugins/allure/src/components/AllureReportComponent/AllureReportComponent.tsx create mode 100644 plugins/allure/src/components/AllureReportComponent/index.ts create mode 100644 plugins/allure/src/components/useAllureProjectId.ts create mode 100644 plugins/allure/src/index.ts create mode 100644 plugins/allure/src/plugin.test.ts create mode 100644 plugins/allure/src/plugin.ts create mode 100644 plugins/allure/src/setupTests.ts diff --git a/app-config.yaml b/app-config.yaml index 9f365becef..c5c4a23bdd 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -133,6 +133,9 @@ kafka: brokers: - localhost:9092 +allure: + baseUrl: http://localhost:5050/allure-docker-service + integrations: github: - host: github.com diff --git a/packages/app/package.json b/packages/app/package.json index 78ebe32e25..a5407abe09 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -10,6 +10,7 @@ "@backstage/core-components": "^0.3.2", "@backstage/core-plugin-api": "^0.1.6", "@backstage/integration-react": "^0.1.7", + "@backstage/plugin-allure": "^0.1.0", "@backstage/plugin-api-docs": "^0.6.6", "@backstage/plugin-badges": "^0.2.7", "@backstage/plugin-catalog": "^0.6.12", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index d84a7341bc..89bc39952f 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -110,6 +110,7 @@ import { } from '@roadiehq/backstage-plugin-travis-ci'; import { EntityCodeCoverageContent } from '@backstage/plugin-code-coverage'; import { EmptyState } from '@backstage/core-components'; +import { EntityAllureReportContent } from '@backstage/plugin-allure'; const EntityLayoutWrapper = (props: { children?: ReactNode }) => { const [badgesDialogOpen, setBadgesDialogOpen] = useState(false); @@ -356,6 +357,10 @@ const serviceEntityPage = ( + + + + ); diff --git a/plugins/allure/.eslintrc.js b/plugins/allure/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/allure/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/allure/README.md b/plugins/allure/README.md new file mode 100644 index 0000000000..d28fe913b1 --- /dev/null +++ b/plugins/allure/README.md @@ -0,0 +1,47 @@ +# Allure + +Welcome to the Backstage Allure plugin. This plugin add an entity service page to display Allure test reports related to the service. + +## Install + +Run the below command from the `app` package directory. + +```shell +yarn add @backstage/plugin-allure +``` + +alternatively, you can execute below command from the root directory of your Backstage app. + +```shell +yarn workspace app add @backstage/plugin-allure +``` + +## Configure + +### Configure Allure service + +Add below configuration in the `app-config.yaml`. + +```yaml +allure: + baseUrl: # Example: http://localhost:5050/allure-docker-service +``` + +### Setup entity service page + +Add `EntityAllureReportContent` in the `EntityPage.tsx` like below: + +```diff ++ import { EntityAllureReportContent } from '@backstage/plugin-allure'; + +... + +const serviceEntityPage = ( + + ... ++ ++ ++ + +); +``` diff --git a/plugins/allure/package.json b/plugins/allure/package.json new file mode 100644 index 0000000000..81a37ed996 --- /dev/null +++ b/plugins/allure/package.json @@ -0,0 +1,68 @@ +{ + "name": "@backstage/plugin-allure", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.3.2", + "@backstage/core-plugin-api": "^0.1.6", + "@backstage/plugin-catalog-react": "^0.4.3", + "@backstage/theme": "^0.2.10", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-router-dom": "6.0.0-beta.0", + "react-use": "^17.2.4" + }, + "devDependencies": { + "@backstage/cli": "^0.7.9", + "@backstage/core-app-api": "^0.1.9", + "@backstage/dev-utils": "^0.2.7", + "@backstage/test-utils": "^0.1.17", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^11.2.5", + "@testing-library/user-event": "^13.1.8", + "@types/jest": "^26.0.7", + "@types/node": "^14.14.32", + "cross-fetch": "^3.0.6", + "msw": "^0.29.0" + }, + "files": [ + "dist" + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "title": "@backstage/allure", + "type": "object", + "properties": { + "allure": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string", + "visibility": "frontend" + } + } + } + } + } +} diff --git a/plugins/allure/src/api/AllureApi.ts b/plugins/allure/src/api/AllureApi.ts new file mode 100644 index 0000000000..a926da03d0 --- /dev/null +++ b/plugins/allure/src/api/AllureApi.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createApiRef } from '@backstage/core-plugin-api'; + +export type AllureApi = { + getReportUrl(projectId: string): Promise; +}; + +export const allureApiRef = createApiRef({ + id: 'allure-api', +}); diff --git a/plugins/allure/src/api/AllureApiClient.ts b/plugins/allure/src/api/AllureApiClient.ts new file mode 100644 index 0000000000..698713c837 --- /dev/null +++ b/plugins/allure/src/api/AllureApiClient.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ConfigApi } from '@backstage/core-plugin-api'; +import { AllureApi } from './AllureApi'; + +export class AllureApiClient implements AllureApi { + readonly configApi: ConfigApi; + + constructor(options: { configApi: ConfigApi }) { + this.configApi = options.configApi; + } + + async getReportUrl(projectId: string): Promise { + const baseUrl = this.configApi.getString('allure.baseUrl'); + return `${baseUrl}/projects/${projectId}/reports/latest/index.html`; + } +} diff --git a/plugins/allure/src/api/index.ts b/plugins/allure/src/api/index.ts new file mode 100644 index 0000000000..1c1adb1db2 --- /dev/null +++ b/plugins/allure/src/api/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { allureApiRef } from './AllureApi'; +export { AllureApiClient } from './AllureApiClient'; diff --git a/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.test.tsx b/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.test.tsx new file mode 100644 index 0000000000..38fa4c0923 --- /dev/null +++ b/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { AllureReportComponent } from './AllureReportComponent'; +import { ThemeProvider } from '@material-ui/core'; +import { lightTheme } from '@backstage/theme'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { msw, renderInTestApp } from '@backstage/test-utils'; + +describe('ExampleComponent', () => { + const server = setupServer(); + // Enable sane handlers for network requests + msw.setupDefaultHandlers(server); + + // setup mock response + beforeEach(() => { + server.use( + rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), + ); + }); + + it('should render', async () => { + const rendered = await renderInTestApp( + + + , + ); + expect(rendered.getByText('Welcome to allure!')).toBeInTheDocument(); + }); +}); diff --git a/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.tsx b/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.tsx new file mode 100644 index 0000000000..c59284b3ef --- /dev/null +++ b/plugins/allure/src/components/AllureReportComponent/AllureReportComponent.tsx @@ -0,0 +1,70 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { useApi } from '@backstage/core-plugin-api'; +import { allureApiRef } from '../../api'; +import { AllureApi } from '../../api/AllureApi'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { + ALLURE_PROJECT_ID_ANNOTATION, + isAllureReportAvailable, + useAllureProjectId, +} from '../useAllureProjectId'; +import { + MissingAnnotationEmptyState, + Progress, +} from '@backstage/core-components'; +import { useAsync } from 'react-use'; +import { Entity } from '@backstage/catalog-model'; + +const AllureReport = (props: { entity: Entity }) => { + const allureApi = useApi(allureApiRef); + + const allureProjectId = useAllureProjectId(props.entity); + const { value, loading } = useAsync(async () => { + const url = await allureApi.getReportUrl(allureProjectId); + return url; + }); + + if (loading) { + return ; + } + return ( + <> + {!loading && ( +