From 7ad9a07b2789e27792b0bb14b0dbf7b6d4ad1554 Mon Sep 17 00:00:00 2001 From: Greg Bomkamp Date: Thu, 28 Oct 2021 10:43:22 -0400 Subject: [PATCH 001/139] Adding config prop `usePluginSchemas` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. Signed-off-by: Greg Bomkamp --- .changeset/pretty-moons-drive.md | 5 + packages/backend-common/config.d.ts | 10 + .../src/database/DatabaseManager.test.ts | 193 +++++++++++++++++- .../src/database/DatabaseManager.ts | 87 ++++++-- .../src/database/connection.test.ts | 75 +++++++ .../backend-common/src/database/connection.ts | 34 +++ .../connectors/defaultSchemaOverride.test.ts | 25 +++ .../connectors/defaultSchemaOverride.ts | 29 +++ .../src/database/connectors/postgres.test.ts | 18 ++ .../src/database/connectors/postgres.ts | 26 +++ packages/backend-common/src/database/types.ts | 17 ++ 11 files changed, 497 insertions(+), 22 deletions(-) create mode 100644 .changeset/pretty-moons-drive.md create mode 100644 packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts create mode 100644 packages/backend-common/src/database/connectors/defaultSchemaOverride.ts diff --git a/.changeset/pretty-moons-drive.md b/.changeset/pretty-moons-drive.md new file mode 100644 index 0000000000..3c3b7968c6 --- /dev/null +++ b/.changeset/pretty-moons-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Adding config prop `usePluginSchemas` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index bc5e026375..4476a946cf 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -69,6 +69,16 @@ export interface Config { * Defaults to true if unspecified. */ ensureExists?: boolean; + /** + * Whether plugins should use their own schemas instead of databases. If enabled, + * each plugin will create a schema in the configured database instance + * using the `pluginId` as its schema name. + * + * NOTE: Currently only supported by the `pg` client. + * + * @default false + */ + usePluginSchemas?: boolean; /** Plugin specific database configuration and client override */ plugin?: { [pluginId: string]: { diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index e839123908..e639f14ee8 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -15,13 +15,18 @@ */ import { ConfigReader } from '@backstage/config'; import { omit } from 'lodash'; -import { createDatabaseClient, ensureDatabaseExists } from './connection'; +import { + createDatabaseClient, + ensureDatabaseExists, + ensureSchemaExists, +} from './connection'; import { DatabaseManager } from './DatabaseManager'; jest.mock('./connection', () => ({ ...jest.requireActual('./connection'), createDatabaseClient: jest.fn(), ensureDatabaseExists: jest.fn(), + ensureSchemaExists: jest.fn(), })); describe('DatabaseManager', () => { @@ -314,5 +319,191 @@ describe('DatabaseManager', () => { expect.stringContaining('userdbname'), ); }); + + it('plugin sets schema override for pg client', async () => { + const overrideConfig = { + backend: { + database: { + client: 'pg', + usePluginSchemas: true, + connection: { + host: 'localhost', + user: 'foo', + password: 'bar', + database: 'foodb', + }, + }, + }, + }; + const testManager = DatabaseManager.fromConfig( + new ConfigReader(overrideConfig), + ); + const pluginId = 'schemaoverride'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [baseConfig, overrides] = mockCalls[0]; + + expect(baseConfig.get()).toMatchObject({ + client: 'pg', + connection: config.backend.database.connection, + }); + + expect(overrides).toMatchObject({ + searchPath: [pluginId], + }); + }); + + it('plugin does not provide schema override for non pg client', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'sqlite3', + usePluginSchemas: true, + connection: { + host: 'localhost', + user: 'foo', + password: 'bar', + database: 'foodb', + }, + }, + }, + }), + ); + const pluginId = 'any-plugin'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [baseConfig, overrides] = mockCalls[0]; + + expect(baseConfig.get()).toMatchObject({ + client: 'sqlite3', + connection: config.backend.database.connection, + }); + + expect(overrides).not.toHaveProperty('searchPath'); + }); + + it('plugin does not provide schema override if usePluginSchemas is not provided', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + usePluginSchemas: false, + connection: 'some-file-path', + }, + }, + }), + ); + + const pluginId = 'any-plugin'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [_baseConfig, overrides] = mockCalls[0]; + + expect(overrides).not.toHaveProperty('searchPath'); + }); + + it('plugin does not provide schema override if usePluginSchemas is false', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + connection: { + host: 'localhost', + user: 'foo', + password: 'bar', + database: 'foodb', + }, + }, + }, + }), + ); + + const pluginId = 'schemaoverride'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [_baseConfig, overrides] = mockCalls[0]; + + expect(overrides).not.toHaveProperty('searchPath'); + }); + + it('usePluginSchemas ensures that each plugin schema exists', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + usePluginSchemas: true, + connection: { + host: 'localhost', + user: 'foo', + password: 'bar', + database: 'foodb', + }, + }, + }, + }), + ); + const pluginId = 'testdbname'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(ensureSchemaExists).mock.calls; + const [_, schemaName] = mockCalls[0]; + + expect(schemaName).toEqual('testdbname'); + }); + + it('usePluginSchemas allows connection overrides for plugins', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + usePluginSchemas: true, + connection: { + host: 'localhost', + user: 'foo', + password: 'bar', + database: 'foodb', + }, + plugin: { + testdbname: { + connection: { + database: 'database_name_overriden', + host: 'newhost', + }, + }, + }, + }, + }, + }), + ); + const pluginId = 'testdbname'; + await testManager.forPlugin(pluginId).getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [baseConfig, overrides] = mockCalls[0]; + + expect(baseConfig.get()).toMatchObject({ + client: 'pg', + connection: { + database: 'database_name_overriden', + host: 'newhost', + user: 'foo', + password: 'bar', + }, + }); + expect(overrides).toHaveProperty('searchPath', ['testdbname']); + expect(overrides).toHaveProperty( + 'connection.database', + 'database_name_overriden', + ); + }); }); }); diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index c0511c6b78..efd779e4ed 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -18,12 +18,15 @@ import { omit } from 'lodash'; import { Config, ConfigReader } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { - createDatabaseClient, ensureDatabaseExists, createNameOverride, normalizeConnection, + createSchemaOverride, + ensureSchemaExists, + createDatabaseClient, } from './connection'; import { PluginDatabaseManager } from './types'; +import { mergeDatabaseConfig } from './config'; /** * Provides a config lookup path for a plugin's config block. @@ -79,14 +82,15 @@ export class DatabaseManager { * Provides the canonical database name for a given plugin. * * This method provides the effective database name which is determined using global - * and plugin specific database config. If no explicit database name is configured, - * this method will provide a generated name which is the pluginId prefixed with - * 'backstage_plugin_'. + * and plugin specific database config. If no explicit database name is configured + * and usePluginSchemas is not enabled, this method will provide a generated name + * which is the pluginId prefixed with 'backstage_plugin_'. If `usePluginSchemas` is + * enabled, it will fallback to using the default database for the knex instance. * * @param pluginId Lookup the database name for given plugin * @returns String representing the plugin's database name */ - private getDatabaseName(pluginId: string): string { + private getDatabaseName(pluginId: string): string | undefined { const connection = this.getConnectionConfig(pluginId); if (this.getClientType(pluginId).client === 'sqlite3') { @@ -95,11 +99,16 @@ export class DatabaseManager { (connection as Knex.Sqlite3ConnectionConfig)?.filename ?? ':memory:' ); } + + const databaseName = (connection as Knex.ConnectionConfig)?.database; + + // usePluginSchemas enabled should use overridden databaseName if supplied or fallback to default knex database + if (this.getUsePluginSchemasConfig()) { + return databaseName; + } + // all other supported databases should fallback to an auto-prefixed name - return ( - (connection as Knex.ConnectionConfig)?.database ?? - `${this.prefix}${pluginId}` - ); + return databaseName ?? `${this.prefix}${pluginId}`; } /** @@ -136,13 +145,18 @@ export class DatabaseManager { ); } + private getUsePluginSchemasConfig(): boolean { + return this.config.getOptionalBoolean('usePluginSchemas') ?? false; + } + /** * Provides a Knex connection plugin config by combining base and plugin config. * * This method provides a baseConfig for a plugin database connector. If the client type * has not been overridden, the global connection config will be included with plugin * specific config as the base. Values from the plugin connection take precedence over the - * base. Base database name is omitted for all supported databases excluding SQLite. + * base. Base database name is omitted for all supported databases excluding SQLite unless + * `usePluginSchemas` is enabled. */ private getConnectionConfig( pluginId: string, @@ -153,10 +167,13 @@ export class DatabaseManager { this.config.get('connection'), this.config.getString('client'), ); - // As databases cannot be shared, the `database` property from the base connection - // is omitted. SQLite3's `filename` property is an exception as this is used as a + // Databases cannot be shared unless the usePluginSchemas is enabled. The + // `database` property from the base connection is omitted unless usePluginSchemas + // is enabled. SQLite3's `filename` property is an exception as this is used as a // directory elsewhere so we preserve `filename`. - baseConnection = omit(baseConnection, 'database'); + if (!this.getUsePluginSchemasConfig()) { + baseConnection = omit(baseConnection, 'database'); + } // get and normalize optional plugin specific database connection const connection = normalizeConnection( @@ -187,6 +204,16 @@ export class DatabaseManager { }; } + /** + * Provides a partial Knex.Config database schema override for a given plugin. + * + * @param pluginId Target plugin to get database schema override + * @returns Partial Knex.Config with database schema override + */ + private getSchemaOverrides(pluginId: string): Knex.Config | undefined { + return createSchemaOverride(this.getClientType(pluginId).client, pluginId); + } + /** * Provides a partial Knex.Config database name override for a given plugin. * @@ -194,10 +221,13 @@ export class DatabaseManager { * @returns Partial Knex.Config with database name override */ private getDatabaseOverrides(pluginId: string): Knex.Config { - return createNameOverride( - this.getClientType(pluginId).client, - this.getDatabaseName(pluginId), - ); + const databaseNameOverride = this.getDatabaseName(pluginId); + return databaseNameOverride + ? createNameOverride( + this.getClientType(pluginId).client, + databaseNameOverride, + ) + : {}; } /** @@ -211,8 +241,8 @@ export class DatabaseManager { this.getConfigForPlugin(pluginId) as JsonObject, ); - if (this.getEnsureExistsConfig(pluginId)) { - const databaseName = this.getDatabaseName(pluginId); + const databaseName = this.getDatabaseName(pluginId); + if (databaseName && this.getEnsureExistsConfig(pluginId)) { try { await ensureDatabaseExists(pluginConfig, databaseName); } catch (error) { @@ -222,9 +252,24 @@ export class DatabaseManager { } } - return createDatabaseClient( - pluginConfig, + let schemaOverrides; + if (this.getUsePluginSchemasConfig()) { + try { + schemaOverrides = this.getSchemaOverrides(pluginId); + await ensureSchemaExists(pluginConfig, pluginId); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, + ); + } + } + + const databaseClientOverrides = mergeDatabaseConfig( + {}, this.getDatabaseOverrides(pluginId), + schemaOverrides, ); + + return createDatabaseClient(pluginConfig, databaseClientOverrides); } } diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts index 869722ddf7..6f02e7b2ed 100644 --- a/packages/backend-common/src/database/connection.test.ts +++ b/packages/backend-common/src/database/connection.test.ts @@ -18,8 +18,24 @@ import { ConfigReader } from '@backstage/config'; import { createDatabaseClient, createNameOverride, + createSchemaOverride, + ensureSchemaExists, parseConnectionString, } from './connection'; +import { pgConnector } from './connectors'; + +const mocked = (f: Function) => f as jest.Mock; + +jest.mock('./connectors', () => { + const connectors = jest.requireActual('./connectors'); + return { + ...connectors, + pgConnector: { + ...connectors.pgConnector, + ensureSchemaExists: jest.fn(), + }, + }; +}); describe('database connection', () => { describe('createDatabaseClient', () => { @@ -152,4 +168,63 @@ describe('database connection', () => { expect(() => parseConnectionString('sqlite://')).toThrow(); }); }); + + describe('createSchemaOverride', () => { + it('returns Knex config for postgres', () => { + expect(createSchemaOverride('pg', 'testpg')).toHaveProperty( + 'searchPath', + ['testpg'], + ); + }); + + it('throws error for sqlite', () => { + expect(createSchemaOverride('sqlite3', 'testsqlite')).toBeUndefined(); + }); + + it('returns Knex config for mysql', () => { + expect(createSchemaOverride('mysql', 'testmysql')).toBeUndefined(); + }); + + it('throws an error for unknown connection', () => { + expect(createSchemaOverride('unknown', 'testname')).toBeUndefined(); + }); + }); + + describe('ensureSchemaExists', () => { + it('returns sucessfully with pg client', async () => { + await ensureSchemaExists( + new ConfigReader({ + client: 'pg', + schema: 'catalog', + connection: 'postgresql://testuser:testpass@acme:5432/userdbname', + }), + 'catalog', + ); + + const mockCalls = mocked( + pgConnector.ensureSchemaExists as Function, + ).mock.calls.splice(-1); + const [baseConfig, schemaName] = mockCalls[0]; + + expect(baseConfig.get()).toMatchObject({ + client: 'pg', + connection: 'postgresql://testuser:testpass@acme:5432/userdbname', + }); + + expect(schemaName).toEqual('catalog'); + }); + + it('throws error for non pg client', () => { + return expect( + ensureSchemaExists( + new ConfigReader({ + client: 'sqlite3', + schema: 'catalog', + connection: ':memory:', + }), + 'catalog', + ), + ).resolves.toBeUndefined(); + }); + }); }); diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index 1c5cce3718..3308a26573 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -82,6 +82,23 @@ export async function ensureDatabaseExists( ); } +/** + * Ensures that the given schemas all exist, creating them if they do not. + * + * @public + */ +export async function ensureSchemaExists( + dbConfig: Config, + ...schemas: Array +): Promise { + const client: DatabaseClient = dbConfig.getString('client'); + + return await ConnectorMapping[client]?.ensureSchemaExists?.( + dbConfig, + ...schemas, + ); +} + /** * Provides a Knex.Config object with the provided database name for a given client. */ @@ -99,6 +116,23 @@ export function createNameOverride( } } +/** + * Provides a Knex.Config object with the provided database schema for a given client. Currently only supported by `pg`. + */ +export function createSchemaOverride( + client: string, + name: string, +): Partial { + try { + return ConnectorMapping[client]?.createSchemaOverride?.(name); + } catch (e) { + throw new InputError( + `Unable to create database schema override for '${client}' connector`, + e, + ); + } +} + /** * Parses a connection string for a given client and provides a connection config. */ diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts new file mode 100644 index 0000000000..2db5ecd454 --- /dev/null +++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts @@ -0,0 +1,25 @@ +/* + * 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 defaultSchemaOverride from './defaultSchemaOverride'; + +describe('defaultNameOverride()', () => { + it('returns a partial knex static connection config with searchPath set to [schemaName]', () => { + const schemaName = 'schemaName'; + expect(defaultSchemaOverride(schemaName)).toHaveProperty('searchPath', [ + schemaName, + ]); + }); +}); diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts new file mode 100644 index 0000000000..62ad8ab8c8 --- /dev/null +++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts @@ -0,0 +1,29 @@ +/* + * 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 { Knex } from 'knex'; + +/** + * Provides a partial knex config with schema name override. + * + * @param name schema name to get config override for + */ +export default function defaultSchemaOverride( + name: string, +): Partial { + return { + searchPath: [name], + }; +} diff --git a/packages/backend-common/src/database/connectors/postgres.test.ts b/packages/backend-common/src/database/connectors/postgres.test.ts index ac988a2880..553d019573 100644 --- a/packages/backend-common/src/database/connectors/postgres.test.ts +++ b/packages/backend-common/src/database/connectors/postgres.test.ts @@ -76,6 +76,24 @@ describe('postgres', () => { }); }); + it('overrides the schema name', () => { + const mockConnection = { + ...createMockConnection(), + schema: 'schemaName', + }; + + expect( + buildPgDatabaseConfig(createConfig(mockConnection), { + searchPath: ['schemaName'], + }), + ).toEqual({ + client: 'pg', + connection: mockConnection, + searchPath: ['schemaName'], + useNullAsDefault: true, + }); + }); + it('adds additional config settings', () => { const mockConnection = createMockConnection(); diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index 62de2e001e..823d50f73f 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -21,6 +21,7 @@ import { ForwardedError } from '@backstage/errors'; import { mergeDatabaseConfig } from '../config'; import { DatabaseConnector } from '../types'; import defaultNameOverride from './defaultNameOverride'; +import defaultSchemaOverride from './defaultSchemaOverride'; /** * Creates a knex postgres database connection @@ -135,6 +136,29 @@ export async function ensurePgDatabaseExists( } } +/** + * Creates the missing Postgres schema if it does not exist + * + * @param dbConfig The database config + * @param schemas The name of the schemas to create + */ +export async function ensurePgSchemaExists( + dbConfig: Config, + ...schemas: Array +): Promise { + const admin = createPgDatabaseClient(dbConfig); + + try { + const ensureSchema = async (database: string) => { + await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]); + }; + + await Promise.all(schemas.map(ensureSchema)); + } finally { + await admin.destroy(); + } +} + /** * PostgreSQL database connector. * @@ -143,6 +167,8 @@ export async function ensurePgDatabaseExists( export const pgConnector: DatabaseConnector = Object.freeze({ createClient: createPgDatabaseClient, ensureDatabaseExists: ensurePgDatabaseExists, + ensureSchemaExists: ensurePgSchemaExists, createNameOverride: defaultNameOverride, + createSchemaOverride: defaultSchemaOverride, parseConnectionString: parsePgConnectionString, }); diff --git a/packages/backend-common/src/database/types.ts b/packages/backend-common/src/database/types.ts index edcba1e643..e96f86980b 100644 --- a/packages/backend-common/src/database/types.ts +++ b/packages/backend-common/src/database/types.ts @@ -45,6 +45,11 @@ export interface DatabaseConnector { * database name. */ createNameOverride(name: string): Partial; + /** + * createSchemaOverride provides a partial knex config sufficient to override a + * PostgreSQL schema name within utilizing the `searchPath` knex configuration. + */ + createSchemaOverride?(name: string): Partial; /** * parseConnectionString produces a knex connection config object representing * a database connection string. @@ -64,4 +69,16 @@ export interface DatabaseConnector { dbConfig: Config, ...databases: Array ): Promise; + + /** + * ensureSchemaExists performs a side-effect to ensure schema names passed in are + * present. + * + * Calling this function on schemas which already exist should do nothing. + * Missing schemas should be created if needed. + */ + ensureSchemaExists?( + dbConfig: Config, + ...schemas: Array + ): Promise; } From 5b9d1291f73366af41cc5494a693d41f3ed07f86 Mon Sep 17 00:00:00 2001 From: Anthony Ikeda Date: Mon, 8 Nov 2021 14:21:44 -0800 Subject: [PATCH 002/139] Placed the Custom Actions pre-requisites at the top of the document instead of the middle. These **pre-requisites** are easily missed if developers are writing custom actions. Signed-off-by: Anthony Ikeda --- .../writing-custom-actions.md | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 07c4891a6c..ff43339ff0 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -8,6 +8,40 @@ If you're wanting to extend the functionality of the Scaffolder, you can do so by writing custom actions which can be used along side our [built-in actions](./builtin-actions.md). +--- +**NOTE** + +When adding custom actions, the actions array will **replace the built-in actions too**, so if you +want to have those as well as your new one, you'll need to do the following: + +```ts +import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; +import { ScmIntegrations } from '@backstage/integration'; + +const integrations = ScmIntegrations.fromConfig(config); + +const builtInActions = createBuiltinActions({ + containerRunner, + integrations, + config, + catalogClient, + reader, +}); + +const actions = [...builtInActions, createNewFileAction()]; + +return await createRouter({ + containerRunner, + logger, + config, + database, + catalogClient, + reader, + actions, +}); +``` +--- + ### Writing your Custom Action Your custom action can live where you choose, but simplest is to include it @@ -126,36 +160,6 @@ return await createRouter({ }); ``` -**NOTE** - the actions array will replace the built-in actions too, so if you -want to have those as well as your new one, you'll need to do the following: - -```ts -import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; -import { ScmIntegrations } from '@backstage/integration'; - -const integrations = ScmIntegrations.fromConfig(config); - -const builtInActions = createBuiltinActions({ - containerRunner, - integrations, - config, - catalogClient, - reader, -}); - -const actions = [...builtInActions, createNewFileAction()]; - -return await createRouter({ - containerRunner, - logger, - config, - database, - catalogClient, - reader, - actions, -}); -``` - ### List of custom action packages Here is a list of Open Source custom actions that you can add to your Backstage From adcb1fa7a2ffc231ad0eda3bd739d427cc013395 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 31 Oct 2021 12:18:33 -0500 Subject: [PATCH 003/139] Added entity pull requests Signed-off-by: Andre Wanlin --- catalog-info.yaml | 2 +- .../app/src/components/catalog/EntityPage.tsx | 17 ++- plugins/azure-devops/README.md | 53 ++++++- plugins/azure-devops/api-report.md | 9 ++ .../azure-devops/docs/azure-devops-builds.png | Bin 177135 -> 177135 bytes .../docs/azure-devops-pull-requests.png | Bin 0 -> 142087 bytes plugins/azure-devops/package.json | 1 + .../azure-devops/src/api/AzureDevOpsApi.ts | 8 ++ .../azure-devops/src/api/AzureDevOpsClient.ts | 13 ++ .../AzurePipelinesIcon/AzurePipelinesIcon.tsx | 54 +++++++ .../components/AzurePipelinesIcon/index.ts | 17 +++ .../AzureReposIcon/AzureReposIcon.tsx | 54 +++++++ .../src/components/AzureReposIcon/index.ts | 17 +++ .../components/BuildTable/BuildTable.test.ts | 7 +- .../src/components/BuildTable/BuildTable.tsx | 61 +++++++- .../EntityPageAzureRepos.tsx | 26 ++++ .../components/EntityPageAzureRepos/index.ts | 17 +++ .../PullRequestStatusButtonGroup.tsx | 69 +++++++++ .../PullRequestStatusButtonGroup/index.ts | 17 +++ .../PullRequestTable/PullRequestTable.tsx | 136 ++++++++++++++++++ .../src/components/PullRequestTable/index.ts | 17 +++ .../azure-devops/src/hooks/usePullRequests.ts | 55 +++++++ .../src/icons/alt-azure-pipelines-icon.svg | 1 + .../src/icons/alt-azure-repos-icon.svg | 1 + plugins/azure-devops/src/index.ts | 1 + plugins/azure-devops/src/plugin.ts | 11 ++ 26 files changed, 654 insertions(+), 10 deletions(-) create mode 100644 plugins/azure-devops/docs/azure-devops-pull-requests.png create mode 100644 plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx create mode 100644 plugins/azure-devops/src/components/AzurePipelinesIcon/index.ts create mode 100644 plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx create mode 100644 plugins/azure-devops/src/components/AzureReposIcon/index.ts create mode 100644 plugins/azure-devops/src/components/EntityPageAzureRepos/EntityPageAzureRepos.tsx create mode 100644 plugins/azure-devops/src/components/EntityPageAzureRepos/index.ts create mode 100644 plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx create mode 100644 plugins/azure-devops/src/components/PullRequestStatusButtonGroup/index.ts create mode 100644 plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx create mode 100644 plugins/azure-devops/src/components/PullRequestTable/index.ts create mode 100644 plugins/azure-devops/src/hooks/usePullRequests.ts create mode 100644 plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg create mode 100644 plugins/azure-devops/src/icons/alt-azure-repos-icon.svg diff --git a/catalog-info.yaml b/catalog-info.yaml index 6de6fd3426..ff894befbc 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -18,6 +18,6 @@ metadata: backstage.io/techdocs-ref: dir:. lighthouse.com/website-url: https://backstage.io spec: - type: library + type: service owner: CNCF lifecycle: experimental diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 3fe027ddad..4f95e89636 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -35,6 +35,7 @@ import { } from '@backstage/plugin-api-docs'; import { EntityAzurePipelinesContent, + EntityAzureReposContent, isAzureDevOpsAvailable, } from '@backstage/plugin-azure-devops'; import { EntityBadgesDialog } from '@backstage/plugin-badges'; @@ -264,6 +265,18 @@ const errorsContent = ( ); +const pullRequestsContent = ( + + + + + + + + + +); + const overviewContent = ( {entityWarningContent} @@ -368,7 +381,7 @@ const serviceEntityPage = ( - + {pullRequestsContent} @@ -427,7 +440,7 @@ const websiteEntityPage = ( - + {pullRequestsContent} diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index 47a0c4debd..f33bd3bdb6 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -6,10 +6,16 @@ Website: [https://dev.azure.com/](https://dev.azure.com/) ### Azure Pipelines -Lists the top _n_ builds for a given repository where _n_ is a configurable value +Lists the top _n_ builds for a given Azure Repo where _n_ is a configurable value ![Azure Pipelines Builds Example](./docs/azure-devops-builds.png) +### Azure Repos + +Lists the top _n_ Active, Completed, or Abandoned Pull Requests for a given repository where _n_ is a configurable value + +![Azure Repos Pull Requests Example](./docs/azure-devops-pull-requests.png) + ## Setup The following sections will help you get the Azure DevOps plugin setup and running @@ -69,14 +75,55 @@ To get the Azure Pipelines component working you'll need to do the following two // ... - // Set defaultLimit to the max number of builds you would like to be able to see - // the default if not set is 10 // ... ``` +**Notes:** + +- The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation +- The `defaultLimit` proper on the `EntityAzurePipelinesContent` will set the max number of Builds you would like to see, if not set this will default to 10 + +### Azure Repos Component + +To get the Azure Repos component working you'll need to do the following two steps: + +1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: + + ```bash + # From your Backstage root directory + cd packages/app + yarn add @backstage/plugin-azure-devops + ``` + +2. Second we need to add the `EntityAzureReposContent` extension to the entity page in your app: + + ```tsx + // In packages/app/src/components/catalog/EntityPage.tsx + import { + EntityAzureReposContent, + isAzureDevOpsAvailable, + } from '@backstage/plugin-azure-devops'; + + // For example in the Service section + const serviceEntityPage = ( + + // ... + + + + // ... + + ``` + +**Notes:** + +- You'll need to add the `EntityLayout.Route` above from step 2 to all the entity sections you want to see Pull Requests in. For example if you wanted to see Pull Requests when looking at Website entities then you would need to add this to the `websiteEntityPage` section. +- The `if` prop is optional on the `EntityLayout.Route`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation +- The `defaultLimit` proper on the `EntityAzureReposContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10 + ## Limitations - Currently multiple organizations are not supported diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index fbe28c179c..fef690ed08 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -28,6 +28,15 @@ export const EntityAzurePipelinesContent: ({ defaultLimit?: number | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityAzureReposContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityAzureReposContent: ({ + defaultLimit, +}: { + defaultLimit?: number | undefined; +}) => JSX.Element; + // Warning: (ae-missing-release-tag) "isAzureDevOpsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/azure-devops/docs/azure-devops-builds.png b/plugins/azure-devops/docs/azure-devops-builds.png index 4788a185ed7d664b179ba6fbd1aaa8453f44a793..6ff93dc7e40be170f9a97bc43f7b5cc81f19b3f7 100644 GIT binary patch delta 149719 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

tQyzotxsgjZw=;Nf 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 z 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 diff --git a/plugins/azure-devops/docs/azure-devops-pull-requests.png b/plugins/azure-devops/docs/azure-devops-pull-requests.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1b2a4267696ba50cd994c622344f4ced7190f0 GIT binary patch literal 142087 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

8J~+Ks*1%rhCD66gJvX<45e(}9@;8@@lx(jo6U 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%0A{HmwPo6r( zxn#R98DZOmm~$Jpe28%ootT`A&&c34H8YD(N=oV?2Jic^W)tNu1ts!?J_#R{M8UV` zYlun~DCxSw@_@(BlOA#WT3BPx{;SLR->r@RhWKDJ%M1p44cvr-Zy#72kN1ts&(D`A za{JqcTVCq58Vxg!=J*{o7{?$__)0E4XlrkmwXm=Nd(#ZPXswoggIpRJ4E$txZ*}#Z z6?!wQ-sYq4(T~h#$VaA(@n=?+A!eV+K=CY_9s?qBoZ9!FAEh(1vc^QsAcvmPo{$_a zjw?ris4f$P;&UdCLwR+)=-DA(8l%MZf#K#Xs7|PcdWQ||APKX^8|}JEnB)9j%%=|I z<_dlmJP)ttj84m?C|1XB{v)E8{9fggkXEu?Nr&pD3E}V`-_Ks3eBf2BZK1|TC;|RQ z4&cgBip^_NT3TZCj3TEBMny%%8jOSwC=?&+`z2In{a?P^Ow-P%jDgUL>lr39iu4%R z>#>yt!${P_LS==8q_g1(m!hyiR^YIXRMpKt79zNuG!1s7xuZwdzzx21bv?+`MTlvv zZ2X8|ToT`{4S62Eb>qej^S%Q94ipqBQ7RIZ`eh)^!^*1=SO9bRT82bpoo7cXufnGu zI2ySIccwqX?Z*U@V6`ID1EkU|%&RWx^_d&9=7al(e*~9vDlFF6Y$sNy@%MUHmxf=m z^6FU`7KzkwE+;|b=3E8}Z{50O#O}h?`phc)4~Tz$++0!cr_<#kDL$Ca44nwN(%>$x zh3|o)%qAzli0t6q@pOp_5l!3UBr^gc>0uv z8rmE{8|0tg1I{4# z-lh2&5Elz)-}~t))P3~79}@rD-=Y7d1tz|7+x^J@lDgeFuO-TOm6L_s%((u@; + + getPullRequests( + projectName: string, + repoName: string, + options?: PullRequestOptions, + ): Promise<{ items: PullRequest[] }>; } diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index 4fd4e781c4..7e566e458f 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -16,6 +16,8 @@ import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; import { + PullRequest, + PullRequestOptions, RepoBuild, RepoBuildOptions, } from '@backstage/plugin-azure-devops-common'; @@ -46,6 +48,17 @@ export class AzureDevOpsClient implements AzureDevOpsApi { return { items }; } + async getPullRequests( + projectName: string, + repoName: string, + options?: PullRequestOptions, + ): Promise<{ items: PullRequest[] }> { + const items = await this.get( + `pull-requests/${projectName}/${repoName}?top=${options?.top}&status=${options?.status}`, + ); + return { items }; + } + private async get(path: string): Promise { const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`; const url = new URL(path, baseUrl); diff --git a/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx b/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx new file mode 100644 index 0000000000..a59a088936 --- /dev/null +++ b/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx @@ -0,0 +1,54 @@ +/* + * 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 { makeStyles, useTheme } from '@material-ui/core'; + +import AzurePipelinesSvg from '../../icons/alt-azure-pipelines-icon.svg'; +import React from 'react'; + +const useStyles = makeStyles({ + darkMode: { + filter: 'invert(100%)', + }, + lightMode: { + filter: 'invert(0%)', + }, +}); + +export const AzurePipelinesIcon = ({ + width, + height, +}: { + width: string; + height: string; +}) => { + const theme = useTheme(); + const classes = useStyles(); + + return ( + Azure Pipelines + ); +}; diff --git a/plugins/azure-devops/src/components/AzurePipelinesIcon/index.ts b/plugins/azure-devops/src/components/AzurePipelinesIcon/index.ts new file mode 100644 index 0000000000..4eb161f7bc --- /dev/null +++ b/plugins/azure-devops/src/components/AzurePipelinesIcon/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 { AzurePipelinesIcon } from './AzurePipelinesIcon'; diff --git a/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx b/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx new file mode 100644 index 0000000000..1f6ea98521 --- /dev/null +++ b/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx @@ -0,0 +1,54 @@ +/* + * 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 { makeStyles, useTheme } from '@material-ui/core'; + +import AzureReposSvg from '../../icons/alt-azure-repos-icon.svg'; +import React from 'react'; + +const useStyles = makeStyles({ + darkMode: { + filter: 'invert(100%)', + }, + lightMode: { + filter: 'invert(0%)', + }, +}); + +export const AzureReposIcon = ({ + width, + height, +}: { + width: string; + height: string; +}) => { + const theme = useTheme(); + const classes = useStyles(); + + return ( + Azure Repos + ); +}; diff --git a/plugins/azure-devops/src/components/AzureReposIcon/index.ts b/plugins/azure-devops/src/components/AzureReposIcon/index.ts new file mode 100644 index 0000000000..2bed6bd5f0 --- /dev/null +++ b/plugins/azure-devops/src/components/AzureReposIcon/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 { AzureReposIcon } from './AzureReposIcon'; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts index cb240aef42..7f7d72b027 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts @@ -18,7 +18,10 @@ import { BuildResult, BuildStatus, } from '@backstage/plugin-azure-devops-common'; -import { getBuildResultComponent, getBuildStateComponent } from './BuildTable'; +import { + getBuildResultComponent, + getBuildStateComponent +} from './BuildTable'; import { renderInTestApp } from '@backstage/test-utils'; @@ -238,4 +241,4 @@ describe('getBuildStateComponent', () => { expect(getByText('Unknown')).toBeInTheDocument(); }); }); -}); +}); \ No newline at end of file diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 5667c65711..27602f9345 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -20,6 +20,7 @@ import { BuildStatus, RepoBuild, } from '@backstage/plugin-azure-devops-common'; +import { DateTime, Interval } from 'luxon' import { Link, ResponseErrorPanel, @@ -33,8 +34,9 @@ import { TableColumn, } from '@backstage/core-components'; -import { DateTime } from 'luxon'; +import { AzurePipelinesIcon } from '../AzurePipelinesIcon'; import React from 'react'; +import humanizeDuration from 'humanize-duration'; export const getBuildResultComponent = (result: number | undefined) => { switch (result) { @@ -113,6 +115,43 @@ export const getBuildStateComponent = ( } }; +export const getBuildDuration = (startTime?: Date, finishTime?: Date) => { + if (!startTime || (!startTime && !finishTime)) { + return ''; + } + + const start = DateTime.fromISO(startTime.toString()); + const finish = finishTime + ? DateTime.fromISO(finishTime.toString()) + : DateTime.now(); + + const formatted = Interval.fromDateTimes(start, finish) + .toDuration() + .valueOf(); + + const shortEnglishHumanizer = humanizeDuration.humanizer({ + language: 'shortEn', + languages: { + shortEn: { + y: () => 'y', + mo: () => 'mo', + w: () => 'w', + d: () => 'd', + h: () => 'h', + m: () => 'm', + s: () => 's', + ms: () => 'ms', + }, + }, + }); + + return shortEnglishHumanizer(formatted, { + largest: 2, + round: true, + spacer: '', + }); +}; + const columns: TableColumn[] = [ { title: 'ID', @@ -144,6 +183,18 @@ const columns: TableColumn[] = [ ), }, + { + title: 'Duration', + field: 'queueTime', + width: 'auto', + render: (row: Partial) => ( + + + {getBuildDuration(row.startTime, row.finishTime)} + + + ), + }, { title: 'Age', field: 'queueTime', @@ -178,7 +229,13 @@ export const BuildTable = ({ items, loading, error }: BuildTableProps) => { pageSize: 5, showEmptyDataSourceMessage: !loading, }} - title={`Builds (${items ? items.length : 0})`} + title={ + + + + Azure Pipelines - Builds ({items ? items.length : 0}) + + } data={items ?? []} /> ); diff --git a/plugins/azure-devops/src/components/EntityPageAzureRepos/EntityPageAzureRepos.tsx b/plugins/azure-devops/src/components/EntityPageAzureRepos/EntityPageAzureRepos.tsx new file mode 100644 index 0000000000..b6050606ff --- /dev/null +++ b/plugins/azure-devops/src/components/EntityPageAzureRepos/EntityPageAzureRepos.tsx @@ -0,0 +1,26 @@ +/* + * 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 { PullRequestTable } from '../PullRequestTable/PullRequestTable'; +import React from 'react'; + +export const EntityPageAzureRepos = ({ + defaultLimit, +}: { + defaultLimit?: number; +}) => { + return ; +}; diff --git a/plugins/azure-devops/src/components/EntityPageAzureRepos/index.ts b/plugins/azure-devops/src/components/EntityPageAzureRepos/index.ts new file mode 100644 index 0000000000..517869027f --- /dev/null +++ b/plugins/azure-devops/src/components/EntityPageAzureRepos/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 { EntityPageAzureRepos } from './EntityPageAzureRepos'; diff --git a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx new file mode 100644 index 0000000000..be0d9cac94 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx @@ -0,0 +1,69 @@ +/* + * 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 { Button, ButtonGroup } from '@material-ui/core'; + +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import React from 'react'; + +export const PullRequestStatusButtonGroup = ({ + pullRequestStatusState, + setPullRequestsStatusState, +}: { + pullRequestStatusState: PullRequestStatus; + setPullRequestsStatusState: any; +}) => { + return ( + + + + + + ); +}; diff --git a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/index.ts b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/index.ts new file mode 100644 index 0000000000..10a667803a --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/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 { PullRequestStatusButtonGroup } from './PullRequestStatusButtonGroup'; diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx new file mode 100644 index 0000000000..8c0e7697f4 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -0,0 +1,136 @@ +/* + * 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 { Box, Chip } from '@material-ui/core'; +import { + Link, + ResponseErrorPanel, + Table, + TableColumn, +} from '@backstage/core-components'; +import React, { useState } from 'react'; + +import { AzureReposIcon } from '../AzureReposIcon'; +import { DateTime } from 'luxon'; +import { PullRequest } from '../../api/types'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequestStatusButtonGroup } from '../PullRequestStatusButtonGroup'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { usePullRequests } from '../../hooks/usePullRequests'; + +const columns: TableColumn[] = [ + { + title: 'ID', + field: 'pullRequestId', + highlight: false, + width: 'auto', + }, + { + title: 'Title', + field: 'title', + width: 'auto', + render: (row: Partial) => ( + + {row.title} + + {row.isDraft && ( + + )} + + + ), + }, + { + title: 'Source', + field: 'sourceRefName', + width: 'auto', + }, + { + title: 'Target', + field: 'targetRefName', + width: 'auto', + }, + { + title: 'Created By', + field: 'createdBy', + width: 'auto', + }, + { + title: 'Created', + field: 'creationDate', + width: 'auto', + render: (row: Partial) => + DateTime.fromISO( + row.creationDate ? row.creationDate.toString() : new Date().toString(), + ).toRelative(), + }, +]; + +type PullRequestTableProps = { + defaultLimit?: number; +}; + +export const PullRequestTable = ({ defaultLimit }: PullRequestTableProps) => { + const [pullRequestStatusState, setPullRequestStatusState] = + useState(PullRequestStatus.Active); + const { entity } = useEntity(); + + const { items, loading, error } = usePullRequests( + entity, + defaultLimit, + pullRequestStatusState, + ); + + if (error) { + return ( +

+ ); + } + + return ( + + + + Azure Repos - Pull Requests ({items ? items.length : 0}) + + + + + } + data={items ?? []} + /> + ); +}; diff --git a/plugins/azure-devops/src/components/PullRequestTable/index.ts b/plugins/azure-devops/src/components/PullRequestTable/index.ts new file mode 100644 index 0000000000..956e8afa2d --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestTable/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 { PullRequestTable } from './PullRequestTable'; diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts new file mode 100644 index 0000000000..eea0c37d58 --- /dev/null +++ b/plugins/azure-devops/src/hooks/usePullRequests.ts @@ -0,0 +1,55 @@ +/* + * 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 { PullRequest, PullRequestOptions } from '../api/types'; + +import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; +import { Entity } from '@backstage/catalog-model'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { azureDevOpsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import { useAsync } from 'react-use'; +import { useProjectRepoFromEntity } from './useProjectRepoFromEntity'; + +export function usePullRequests( + entity: Entity, + defaultLimit?: number, + requestedStatus?: PullRequestStatus, +): { + items?: PullRequest[]; + loading: boolean; + error?: Error; +} { + const top = defaultLimit ?? AZURE_DEVOPS_DEFAULT_TOP; + const status = requestedStatus ?? PullRequestStatus.Active; + const options: PullRequestOptions = { + top: top, + status: status, + }; + + const api = useApi(azureDevOpsApiRef); + const { project, repo } = useProjectRepoFromEntity(entity); + + const { value, loading, error } = useAsync(() => { + return api.getPullRequests(project, repo, options); + }, [api, project, repo, entity, requestedStatus]); + + return { + items: value?.items, + loading, + error, + }; +} diff --git a/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg b/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg new file mode 100644 index 0000000000..9dd1776b5e --- /dev/null +++ b/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg @@ -0,0 +1 @@ +Rocket \ No newline at end of file diff --git a/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg b/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg new file mode 100644 index 0000000000..1b2fb4415f --- /dev/null +++ b/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg @@ -0,0 +1 @@ +Git Pull Request \ No newline at end of file diff --git a/plugins/azure-devops/src/index.ts b/plugins/azure-devops/src/index.ts index 71d3b654fe..94cc4ff367 100644 --- a/plugins/azure-devops/src/index.ts +++ b/plugins/azure-devops/src/index.ts @@ -16,5 +16,6 @@ export { azureDevOpsPlugin, EntityAzurePipelinesContent, + EntityAzureReposContent, isAzureDevOpsAvailable, } from './plugin'; diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts index ab6d816460..595a168d6e 100644 --- a/plugins/azure-devops/src/plugin.ts +++ b/plugins/azure-devops/src/plugin.ts @@ -56,3 +56,14 @@ export const EntityAzurePipelinesContent = azureDevOpsPlugin.provide( mountPoint: azureDevOpsRouteRef, }), ); + +export const EntityAzureReposContent = azureDevOpsPlugin.provide( + createRoutableExtension({ + name: 'EntityAzureReposContent', + component: () => + import('./components/EntityPageAzureRepos').then( + m => m.EntityPageAzureRepos, + ), + mountPoint: azureDevOpsRouteRef, + }), +); From b5eac957f22fa5e76ac16cd9ec463d3e14e156e6 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 31 Oct 2021 12:23:49 -0500 Subject: [PATCH 004/139] Added changeset Signed-off-by: Andre Wanlin --- .changeset/new-cobras-fly.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/new-cobras-fly.md diff --git a/.changeset/new-cobras-fly.md b/.changeset/new-cobras-fly.md new file mode 100644 index 0000000000..f39f1b0fbb --- /dev/null +++ b/.changeset/new-cobras-fly.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Added entity view for Azure Repo Pull Requests From 920fcd7fadb82893ac671a002a36a8459e8c5d25 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sun, 31 Oct 2021 13:54:09 -0500 Subject: [PATCH 005/139] Reverted accidental change Signed-off-by: Andre Wanlin --- catalog-info.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index ff894befbc..6de6fd3426 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -18,6 +18,6 @@ metadata: backstage.io/techdocs-ref: dir:. lighthouse.com/website-url: https://backstage.io spec: - type: service + type: library owner: CNCF lifecycle: experimental From 12d43328eabed039dd0f07b077af4c8446db7e0f Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 3 Nov 2021 08:11:36 -0500 Subject: [PATCH 006/139] Clean up based on feedback Signed-off-by: Andre Wanlin --- .../azure-devops/src/api/AzureDevOpsClient.ts | 4 ++-- .../components/BuildTable/BuildTable.test.ts | 22 ++++++++++++++++++- .../src/components/BuildTable/BuildTable.tsx | 5 ++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index 7e566e458f..ec727f4973 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -37,7 +37,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi { this.identityApi = options.identityApi; } - async getRepoBuilds( + public async getRepoBuilds( projectName: string, repoName: string, options?: RepoBuildOptions, @@ -48,7 +48,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi { return { items }; } - async getPullRequests( + public async getPullRequests( projectName: string, repoName: string, options?: PullRequestOptions, diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts index 7f7d72b027..3797cb65d6 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts @@ -241,4 +241,24 @@ describe('getBuildStateComponent', () => { expect(getByText('Unknown')).toBeInTheDocument(); }); }); -}); \ No newline at end of file +}); + +describe('getBuildDuration', () => { + describe('getBuildDuration with undefined startTime and valid finishTime', () => { + it('should return empty result', () => { + const finishTime = new Date('2021-10-15T11:00:00.0000000Z'); + + const result = getBuildDuration(undefined, finishTime); + + expect(result).toEqual(''); + }); + }); + + describe('getBuildDuration with undefined startTime and undefined finishTime', () => { + it('should return empty result', () => { + const result = getBuildDuration(undefined, undefined); + + expect(result).toEqual(''); + }); + }); +}); diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 27602f9345..6166c81aa6 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -115,7 +115,10 @@ export const getBuildStateComponent = ( } }; -export const getBuildDuration = (startTime?: Date, finishTime?: Date) => { +export const getBuildDuration = ( + startTime?: Date, + finishTime?: Date, +): string => { if (!startTime || (!startTime && !finishTime)) { return ''; } From a9b7481cd3a08e0b0bb1ae4594e55b9cbb47b039 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 3 Nov 2021 08:52:42 -0500 Subject: [PATCH 007/139] Refactored icons based on feedback Signed-off-by: Andre Wanlin --- .../AzurePipelinesIcon/AzurePipelinesIcon.tsx | 54 +++++------- .../AzureReposIcon/AzureReposIcon.tsx | 84 +++++++++++-------- .../src/components/BuildTable/BuildTable.tsx | 2 +- .../PullRequestTable/PullRequestTable.tsx | 2 +- .../src/icons/alt-azure-pipelines-icon.svg | 1 - .../src/icons/alt-azure-repos-icon.svg | 1 - 6 files changed, 72 insertions(+), 72 deletions(-) delete mode 100644 plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg delete mode 100644 plugins/azure-devops/src/icons/alt-azure-repos-icon.svg diff --git a/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx b/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx index a59a088936..bd19331f6e 100644 --- a/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx +++ b/plugins/azure-devops/src/components/AzurePipelinesIcon/AzurePipelinesIcon.tsx @@ -14,41 +14,27 @@ * limitations under the License. */ -import { makeStyles, useTheme } from '@material-ui/core'; +import { SvgIcon, SvgIconProps } from '@material-ui/core'; -import AzurePipelinesSvg from '../../icons/alt-azure-pipelines-icon.svg'; import React from 'react'; -const useStyles = makeStyles({ - darkMode: { - filter: 'invert(100%)', - }, - lightMode: { - filter: 'invert(0%)', - }, -}); - -export const AzurePipelinesIcon = ({ - width, - height, -}: { - width: string; - height: string; -}) => { - const theme = useTheme(); - const classes = useStyles(); - - return ( - Azure Pipelines ( + + - ); -}; + + +); diff --git a/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx b/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx index 1f6ea98521..ca0bd2bcf6 100644 --- a/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx +++ b/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx @@ -14,41 +14,57 @@ * limitations under the License. */ -import { makeStyles, useTheme } from '@material-ui/core'; +import { SvgIcon, SvgIconProps } from '@material-ui/core'; -import AzureReposSvg from '../../icons/alt-azure-repos-icon.svg'; import React from 'react'; -const useStyles = makeStyles({ - darkMode: { - filter: 'invert(100%)', - }, - lightMode: { - filter: 'invert(0%)', - }, -}); - -export const AzureReposIcon = ({ - width, - height, -}: { - width: string; - height: string; -}) => { - const theme = useTheme(); - const classes = useStyles(); - - return ( - Azure Repos ( + + - ); -}; + + + + + +); diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 6166c81aa6..94d02d15cd 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -234,7 +234,7 @@ export const BuildTable = ({ items, loading, error }: BuildTableProps) => { }} title={ - + Azure Pipelines - Builds ({items ? items.length : 0}) diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx index 8c0e7697f4..52c8d98a97 100644 --- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -119,7 +119,7 @@ export const PullRequestTable = ({ defaultLimit }: PullRequestTableProps) => { }} title={ - + Azure Repos - Pull Requests ({items ? items.length : 0}) diff --git a/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg b/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg deleted file mode 100644 index 9dd1776b5e..0000000000 --- a/plugins/azure-devops/src/icons/alt-azure-pipelines-icon.svg +++ /dev/null @@ -1 +0,0 @@ -Rocket \ No newline at end of file diff --git a/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg b/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg deleted file mode 100644 index 1b2fb4415f..0000000000 --- a/plugins/azure-devops/src/icons/alt-azure-repos-icon.svg +++ /dev/null @@ -1 +0,0 @@ -Git Pull Request \ No newline at end of file From 7468647b37542d29df4bdbf083d7d4f098d1a1ed Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 3 Nov 2021 09:28:34 -0500 Subject: [PATCH 008/139] Extracted duration function and related tests Signed-off-by: Andre Wanlin --- .../components/BuildTable/BuildTable.test.ts | 20 ------- .../src/components/BuildTable/BuildTable.tsx | 46 +-------------- .../src/utils/getDurationFromDates.test.ts | 35 +++++++++++ .../src/utils/getDurationFromDates.ts | 58 +++++++++++++++++++ 4 files changed, 96 insertions(+), 63 deletions(-) create mode 100644 plugins/azure-devops/src/utils/getDurationFromDates.test.ts create mode 100644 plugins/azure-devops/src/utils/getDurationFromDates.ts diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts index 3797cb65d6..22476b334e 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts @@ -242,23 +242,3 @@ describe('getBuildStateComponent', () => { }); }); }); - -describe('getBuildDuration', () => { - describe('getBuildDuration with undefined startTime and valid finishTime', () => { - it('should return empty result', () => { - const finishTime = new Date('2021-10-15T11:00:00.0000000Z'); - - const result = getBuildDuration(undefined, finishTime); - - expect(result).toEqual(''); - }); - }); - - describe('getBuildDuration with undefined startTime and undefined finishTime', () => { - it('should return empty result', () => { - const result = getBuildDuration(undefined, undefined); - - expect(result).toEqual(''); - }); - }); -}); diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 94d02d15cd..cca4b4a92e 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -20,7 +20,6 @@ import { BuildStatus, RepoBuild, } from '@backstage/plugin-azure-devops-common'; -import { DateTime, Interval } from 'luxon' import { Link, ResponseErrorPanel, @@ -35,8 +34,9 @@ import { } from '@backstage/core-components'; import { AzurePipelinesIcon } from '../AzurePipelinesIcon'; +import { DateTime } from 'luxon'; import React from 'react'; -import humanizeDuration from 'humanize-duration'; +import { getDurationFromDates } from '../../utils/getDurationFromDates'; export const getBuildResultComponent = (result: number | undefined) => { switch (result) { @@ -115,46 +115,6 @@ export const getBuildStateComponent = ( } }; -export const getBuildDuration = ( - startTime?: Date, - finishTime?: Date, -): string => { - if (!startTime || (!startTime && !finishTime)) { - return ''; - } - - const start = DateTime.fromISO(startTime.toString()); - const finish = finishTime - ? DateTime.fromISO(finishTime.toString()) - : DateTime.now(); - - const formatted = Interval.fromDateTimes(start, finish) - .toDuration() - .valueOf(); - - const shortEnglishHumanizer = humanizeDuration.humanizer({ - language: 'shortEn', - languages: { - shortEn: { - y: () => 'y', - mo: () => 'mo', - w: () => 'w', - d: () => 'd', - h: () => 'h', - m: () => 'm', - s: () => 's', - ms: () => 'ms', - }, - }, - }); - - return shortEnglishHumanizer(formatted, { - largest: 2, - round: true, - spacer: '', - }); -}; - const columns: TableColumn[] = [ { title: 'ID', @@ -193,7 +153,7 @@ const columns: TableColumn[] = [ render: (row: Partial) => ( - {getBuildDuration(row.startTime, row.finishTime)} + {getDurationFromDates(row.startTime, row.finishTime)} ), diff --git a/plugins/azure-devops/src/utils/getDurationFromDates.test.ts b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts new file mode 100644 index 0000000000..36763b7cba --- /dev/null +++ b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts @@ -0,0 +1,35 @@ +/* + * 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 { getDurationFromDates } from './getDurationFromDates'; + +describe('getDurationFromDates with undefined startTime and valid finishTime', () => { + it('should return empty result', () => { + const finishTime = new Date('2021-10-15T11:00:00.0000000Z'); + + const result = getDurationFromDates(undefined, finishTime); + + expect(result).toEqual(''); + }); +}); + +describe('getDurationFromDates with undefined startTime and undefined finishTime', () => { + it('should return empty result', () => { + const result = getDurationFromDates(undefined, undefined); + + expect(result).toEqual(''); + }); +}); diff --git a/plugins/azure-devops/src/utils/getDurationFromDates.ts b/plugins/azure-devops/src/utils/getDurationFromDates.ts new file mode 100644 index 0000000000..5b1fb60219 --- /dev/null +++ b/plugins/azure-devops/src/utils/getDurationFromDates.ts @@ -0,0 +1,58 @@ +/* + * 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 { DateTime, Interval } from 'luxon'; +import humanizeDuration from 'humanize-duration'; + +export const getDurationFromDates = ( + startTime?: Date, + finishTime?: Date, +): string => { + if (!startTime || (!startTime && !finishTime)) { + return ''; + } + + const start = DateTime.fromISO(startTime.toString()); + const finish = finishTime + ? DateTime.fromISO(finishTime.toString()) + : DateTime.now(); + + const formatted = Interval.fromDateTimes(start, finish) + .toDuration() + .valueOf(); + + const shortEnglishHumanizer = humanizeDuration.humanizer({ + language: 'shortEn', + languages: { + shortEn: { + y: () => 'y', + mo: () => 'mo', + w: () => 'w', + d: () => 'd', + h: () => 'h', + m: () => 'm', + s: () => 's', + ms: () => 'ms', + }, + }, + }); + + return shortEnglishHumanizer(formatted, { + largest: 2, + round: true, + spacer: '', + }); +}; From aff67e97eb83802872789552c6052dd53fcf26a2 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Mon, 8 Nov 2021 11:41:52 -0600 Subject: [PATCH 009/139] Small refactor based on feedback Signed-off-by: Andre Wanlin --- .../src/components/PullRequestTable/PullRequestTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx index 52c8d98a97..f91fc34c58 100644 --- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -79,7 +79,7 @@ const columns: TableColumn[] = [ width: 'auto', render: (row: Partial) => DateTime.fromISO( - row.creationDate ? row.creationDate.toString() : new Date().toString(), + (row.creationDate ?? new Date()).toString(), ).toRelative(), }, ]; From 9ae9f6cf75a95846a39882d7921fbd6f7d6cb2b3 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 07:31:11 -0600 Subject: [PATCH 010/139] Minor changes after rebase to latest master Signed-off-by: Andre Wanlin --- .../src/components/PullRequestTable/PullRequestTable.tsx | 6 ++++-- plugins/azure-devops/src/hooks/usePullRequests.ts | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx index f91fc34c58..81cf3fd56a 100644 --- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -21,12 +21,14 @@ import { Table, TableColumn, } from '@backstage/core-components'; +import { + PullRequest, + PullRequestStatus, +} from '@backstage/plugin-azure-devops-common'; import React, { useState } from 'react'; import { AzureReposIcon } from '../AzureReposIcon'; import { DateTime } from 'luxon'; -import { PullRequest } from '../../api/types'; -import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { PullRequestStatusButtonGroup } from '../PullRequestStatusButtonGroup'; import { useEntity } from '@backstage/plugin-catalog-react'; import { usePullRequests } from '../../hooks/usePullRequests'; diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts index eea0c37d58..687478453e 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.ts +++ b/plugins/azure-devops/src/hooks/usePullRequests.ts @@ -14,11 +14,14 @@ * limitations under the License. */ -import { PullRequest, PullRequestOptions } from '../api/types'; +import { + PullRequest, + PullRequestOptions, + PullRequestStatus, +} from '@backstage/plugin-azure-devops-common'; import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { Entity } from '@backstage/catalog-model'; -import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import { useAsync } from 'react-use'; From 6d56f20ae6e5184d00aa89a149bdde9ce532ba4b Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 07:46:29 -0600 Subject: [PATCH 011/139] Improved naming based on feedback and a few other improvements Signed-off-by: Andre Wanlin --- packages/app/src/components/catalog/EntityPage.tsx | 4 ++-- plugins/azure-devops/api-report.md | 4 ++-- .../AzurePullRequestsIcon.tsx} | 2 +- .../{AzureReposIcon => AzurePullRequestsIcon}/index.ts | 2 +- .../EntityPageAzurePullRequests.tsx} | 2 +- .../index.ts | 2 +- .../src/components/PullRequestTable/PullRequestTable.tsx | 6 +++--- plugins/azure-devops/src/index.ts | 2 +- plugins/azure-devops/src/plugin.ts | 8 ++++---- 9 files changed, 16 insertions(+), 16 deletions(-) rename plugins/azure-devops/src/components/{AzureReposIcon/AzureReposIcon.tsx => AzurePullRequestsIcon/AzurePullRequestsIcon.tsx} (96%) rename plugins/azure-devops/src/components/{AzureReposIcon => AzurePullRequestsIcon}/index.ts (90%) rename plugins/azure-devops/src/components/{EntityPageAzureRepos/EntityPageAzureRepos.tsx => EntityPageAzurePullRequests/EntityPageAzurePullRequests.tsx} (94%) rename plugins/azure-devops/src/components/{EntityPageAzureRepos => EntityPageAzurePullRequests}/index.ts (88%) diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 4f95e89636..3b9b381380 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -35,7 +35,7 @@ import { } from '@backstage/plugin-api-docs'; import { EntityAzurePipelinesContent, - EntityAzureReposContent, + EntityAzurePullRequestsContent, isAzureDevOpsAvailable, } from '@backstage/plugin-azure-devops'; import { EntityBadgesDialog } from '@backstage/plugin-badges'; @@ -268,7 +268,7 @@ const errorsContent = ( const pullRequestsContent = ( - + diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index fef690ed08..578c2492a9 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -28,10 +28,10 @@ export const EntityAzurePipelinesContent: ({ defaultLimit?: number | undefined; }) => JSX.Element; -// Warning: (ae-missing-release-tag) "EntityAzureReposContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "EntityAzurePullRequestsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const EntityAzureReposContent: ({ +export const EntityAzurePullRequestsContent: ({ defaultLimit, }: { defaultLimit?: number | undefined; diff --git a/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx b/plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx similarity index 96% rename from plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx rename to plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx index ca0bd2bcf6..bb4cb8c081 100644 --- a/plugins/azure-devops/src/components/AzureReposIcon/AzureReposIcon.tsx +++ b/plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx @@ -18,7 +18,7 @@ import { SvgIcon, SvgIconProps } from '@material-ui/core'; import React from 'react'; -export const AzureReposIcon = (props: SvgIconProps) => ( +export const AzurePullRequestsIcon = (props: SvgIconProps) => ( ) => ( - {row.title} + {row.title} {row.isDraft && ( { }} title={ - + Azure Repos - Pull Requests ({items ? items.length : 0}) diff --git a/plugins/azure-devops/src/index.ts b/plugins/azure-devops/src/index.ts index 94cc4ff367..4da533b534 100644 --- a/plugins/azure-devops/src/index.ts +++ b/plugins/azure-devops/src/index.ts @@ -16,6 +16,6 @@ export { azureDevOpsPlugin, EntityAzurePipelinesContent, - EntityAzureReposContent, + EntityAzurePullRequestsContent, isAzureDevOpsAvailable, } from './plugin'; diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts index 595a168d6e..55ec293360 100644 --- a/plugins/azure-devops/src/plugin.ts +++ b/plugins/azure-devops/src/plugin.ts @@ -57,12 +57,12 @@ export const EntityAzurePipelinesContent = azureDevOpsPlugin.provide( }), ); -export const EntityAzureReposContent = azureDevOpsPlugin.provide( +export const EntityAzurePullRequestsContent = azureDevOpsPlugin.provide( createRoutableExtension({ - name: 'EntityAzureReposContent', + name: 'EntityAzurePullRequestsContent', component: () => - import('./components/EntityPageAzureRepos').then( - m => m.EntityPageAzureRepos, + import('./components/EntityPageAzurePullRequests').then( + m => m.EntityPageAzurePullRequests, ), mountPoint: azureDevOpsRouteRef, }), From 5985135e250b1d9e405ae5716a8a062d012014ca Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 07:53:32 -0600 Subject: [PATCH 012/139] Moved box to be inside row,isDraft condition Signed-off-by: Andre Wanlin --- .../src/components/PullRequestTable/PullRequestTable.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx index ad744f2cb7..a898766394 100644 --- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -47,16 +47,16 @@ const columns: TableColumn[] = [ render: (row: Partial) => ( {row.title} - - {row.isDraft && ( + {row.isDraft && ( + - )} - + + )} ), }, From 8671e8e9f9e7fef7e473921361aaa8749beba534 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 08:05:53 -0600 Subject: [PATCH 013/139] Ran prettier to fix CI failure Signed-off-by: Andre Wanlin --- .../src/components/BuildTable/BuildTable.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts index 22476b334e..cb240aef42 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts @@ -18,10 +18,7 @@ import { BuildResult, BuildStatus, } from '@backstage/plugin-azure-devops-common'; -import { - getBuildResultComponent, - getBuildStateComponent -} from './BuildTable'; +import { getBuildResultComponent, getBuildStateComponent } from './BuildTable'; import { renderInTestApp } from '@backstage/test-utils'; From df996334277022afffefaa4fc216f45614e27e4e Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 08:19:47 -0600 Subject: [PATCH 014/139] Updated to import from azure-devops-common Signed-off-by: Andre Wanlin --- .../PullRequestStatusButtonGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx index be0d9cac94..8cc476f5cf 100644 --- a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx +++ b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx @@ -16,7 +16,7 @@ import { Button, ButtonGroup } from '@material-ui/core'; -import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequestStatus } from '@backstage/plugin-azure-devops-common'; import React from 'react'; export const PullRequestStatusButtonGroup = ({ From 486b0251b70eb13392988f9c1633cff1611db29a Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 10 Nov 2021 10:25:07 -0600 Subject: [PATCH 015/139] Provided proper type Signed-off-by: Andre Wanlin --- .../PullRequestStatusButtonGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx index 8cc476f5cf..23a8738adc 100644 --- a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx +++ b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx @@ -24,7 +24,7 @@ export const PullRequestStatusButtonGroup = ({ setPullRequestsStatusState, }: { pullRequestStatusState: PullRequestStatus; - setPullRequestsStatusState: any; + setPullRequestsStatusState: (pullRequestStatus: PullRequestStatus) => void; }) => { return ( From 677da179d83a014573062230c54768449320cd46 Mon Sep 17 00:00:00 2001 From: Anthony Ikeda Date: Tue, 9 Nov 2021 11:07:14 -0800 Subject: [PATCH 016/139] More consistent formatting of the Note block Signed-off-by: Anthony Ikeda --- docs/features/software-templates/writing-custom-actions.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index ff43339ff0..93cd2a7db1 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -8,11 +8,9 @@ If you're wanting to extend the functionality of the Scaffolder, you can do so by writing custom actions which can be used along side our [built-in actions](./builtin-actions.md). ---- -**NOTE** -When adding custom actions, the actions array will **replace the built-in actions too**, so if you -want to have those as well as your new one, you'll need to do the following: +> Note: When adding custom actions, the actions array will **replace the built-in actions too**, +> so if you want to have those as well as your new one, you'll need to do the following: ```ts import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; @@ -40,7 +38,6 @@ return await createRouter({ actions, }); ``` ---- ### Writing your Custom Action From 02ebae6ffd559a5a2ceb82fcf678f97ddf76fc98 Mon Sep 17 00:00:00 2001 From: Anthony Ikeda Date: Tue, 9 Nov 2021 11:58:58 -0800 Subject: [PATCH 017/139] Removed code block at top and altered the main registration code block to include the builtInActions registration Signed-off-by: Anthony Ikeda --- .../writing-custom-actions.md | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 93cd2a7db1..4663b2abb6 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -10,34 +10,9 @@ by writing custom actions which can be used along side our > Note: When adding custom actions, the actions array will **replace the built-in actions too**, -> so if you want to have those as well as your new one, you'll need to do the following: +> so if you want to have those as well as your new one. +> See below to include `builtInActions` during registration of your action. -```ts -import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; -import { ScmIntegrations } from '@backstage/integration'; - -const integrations = ScmIntegrations.fromConfig(config); - -const builtInActions = createBuiltinActions({ - containerRunner, - integrations, - config, - catalogClient, - reader, -}); - -const actions = [...builtInActions, createNewFileAction()]; - -return await createRouter({ - containerRunner, - logger, - config, - database, - catalogClient, - reader, - actions, -}); -``` ### Writing your Custom Action @@ -145,7 +120,17 @@ There's another property you can pass here, which is an array of `actions` which will set the available actions that the scaffolder has access to. ```ts -const actions = [createNewFileAction()]; +const integrations = ScmIntegrations.fromConfig(config); + +const builtInActions = createBuiltinActions({ + containerRunner, + integrations, + config, + catalogClient, + reader, +}); + +const actions = [...builtInActions, createNewFileAction()]; return await createRouter({ containerRunner, logger, From ac4eeb710259b9216c19e48ee5fb3faa7aa7c122 Mon Sep 17 00:00:00 2001 From: Anthony Ikeda Date: Tue, 9 Nov 2021 11:07:14 -0800 Subject: [PATCH 018/139] More consistent formatting of the Note block Signed-off-by: Anthony Ikeda --- .../writing-custom-actions.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 4663b2abb6..d3dff2b4e8 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -10,9 +10,40 @@ by writing custom actions which can be used along side our > Note: When adding custom actions, the actions array will **replace the built-in actions too**, +<<<<<<< HEAD > so if you want to have those as well as your new one. > See below to include `builtInActions` during registration of your action. +======= +> so if you want to have those as well as your new one, you'll need to do the following: + +```ts +import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; +import { ScmIntegrations } from '@backstage/integration'; + +const integrations = ScmIntegrations.fromConfig(config); + +const builtInActions = createBuiltinActions({ + containerRunner, + integrations, + config, + catalogClient, + reader, +}); + +const actions = [...builtInActions, createNewFileAction()]; + +return await createRouter({ + containerRunner, + logger, + config, + database, + catalogClient, + reader, + actions, +}); +``` +>>>>>>> a72c8bf39a (More consistent formatting of the Note block) ### Writing your Custom Action From 110166bc8a33607833ad0ebbff12e6a1465e04cc Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 11 Nov 2021 10:39:53 +0100 Subject: [PATCH 019/139] focus search input field on mount Signed-off-by: Emma Indal --- plugins/search/src/components/SearchBar/SearchBar.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index 53802899eb..8d493df5f8 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useEffect, KeyboardEvent, useState } from 'react'; +import React, { useEffect, KeyboardEvent, useState, useRef } from 'react'; import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { useDebounce } from 'react-use'; import { InputBase, InputAdornment, IconButton } from '@material-ui/core'; @@ -40,6 +40,11 @@ export const SearchBarBase = ({ placeholder: overridePlaceholder, }: PresenterProps) => { const configApi = useApi(configApiRef); + const inputRef = useRef(); + + useEffect(() => { + inputRef?.current?.focus(); + }, []); const onKeyDown = React.useCallback( (e: KeyboardEvent) => { @@ -60,6 +65,7 @@ export const SearchBarBase = ({ return ( Date: Thu, 11 Nov 2021 10:41:58 +0100 Subject: [PATCH 020/139] decrease debounce time Signed-off-by: Emma Indal --- plugins/search/src/components/SearchModal/SearchModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index feabbc55af..37cb93b6d3 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -68,7 +68,7 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => { setValue(prevValue => (prevValue !== term ? term : prevValue)); }, [term]); - useDebounce(() => setTerm(value), 1000, [value]); + useDebounce(() => setTerm(value), 500, [value]); const handleQuery = (newValue: string) => { setValue(newValue); From e44d62dfe76a4ed7da742d803eccfa87668bb290 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 11 Nov 2021 10:43:05 +0100 Subject: [PATCH 021/139] close modal on view all results Signed-off-by: Emma Indal --- plugins/search/src/components/SearchModal/SearchModal.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index 37cb93b6d3..75d4b07a3c 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -114,7 +114,10 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => { alignItems="center" > - + View Full Results From b0efce13becbb8e46115c4bc02b8a00124dcb066 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 11 Nov 2021 10:44:11 +0100 Subject: [PATCH 022/139] update terminology of pager buttons, update size and margin Signed-off-by: Emma Indal --- .../src/components/SearchResultPager/SearchResultPager.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx b/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx index f1850772bb..12f07530ef 100644 --- a/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx +++ b/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx @@ -25,7 +25,7 @@ const useStyles = makeStyles(theme => ({ display: 'flex', justifyContent: 'space-between', gap: theme.spacing(2), - margin: theme.spacing(4), + margin: theme.spacing(2, 0), }, })); @@ -44,9 +44,8 @@ export const SearchResultPager = () => { disabled={!fetchPreviousPage} onClick={fetchPreviousPage} startIcon={} - size="small" > - Back + Previous From abe2cc4b00f00c441e677e04692c743709432b8b Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 11 Nov 2021 11:34:32 +0100 Subject: [PATCH 023/139] add search modal to storybook Signed-off-by: Emma Indal --- .../SearchModal/SearchModal.stories.tsx | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 plugins/search/src/components/SearchModal/SearchModal.stories.tsx diff --git a/plugins/search/src/components/SearchModal/SearchModal.stories.tsx b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx new file mode 100644 index 0000000000..5eefad8e0d --- /dev/null +++ b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState, ComponentType } from 'react'; +import { Button } from '@material-ui/core'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { SearchModal } from '../index'; +import { searchApiRef } from '../../apis'; +import { rootRouteRef } from '../../plugin'; + +export default { + title: 'Plugins/Search/SearchModal', + component: SearchModal, + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + <> + + , + { mountedRoutes: { '/search': rootRouteRef } }, + ), + ], +}; + +const mockSearchApi = { + query: () => + Promise.resolve({ + results: [ + { + type: 'custom-result-item', + document: { + location: 'search/search-result-1', + title: 'Search Result 1', + text: 'some text from the search result', + }, + }, + { + type: 'no-custom-result-item', + document: { + location: 'search/search-result-2', + title: 'Search Result 2', + text: 'some text from the search result', + }, + }, + { + type: 'no-custom-result-item', + document: { + location: 'search/search-result-3', + title: 'Search Result 3', + text: 'some text from the search result', + }, + }, + ], + }), +}; + +const apiRegistry = () => ApiRegistry.from([[searchApiRef, mockSearchApi]]); + +export const Default = () => { + const [open, setOpen] = useState(false); + const toggleModal = (): void => setOpen(prevState => !prevState); + + return ( + + + + + ); +}; From 704b267e1c794e81bb5eed09dd24301b920c6928 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 11 Nov 2021 11:43:25 +0100 Subject: [PATCH 024/139] add changeset Signed-off-by: Emma Indal --- .changeset/search-serious-hornets-design.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/search-serious-hornets-design.md diff --git a/.changeset/search-serious-hornets-design.md b/.changeset/search-serious-hornets-design.md new file mode 100644 index 0000000000..5fc47f00aa --- /dev/null +++ b/.changeset/search-serious-hornets-design.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +Smaller UX improvements such as focused input field, decreased debounce value, closing modal on link click, terminology updates. From 7715cadf2daa5c7f8065c96795c7baf3a8c19999 Mon Sep 17 00:00:00 2001 From: Greg Bomkamp Date: Thu, 11 Nov 2021 10:37:27 -0500 Subject: [PATCH 025/139] Update config option from usePluginSchemas to pluginDivisionMode, Clean up Var names for clarify, fix tests Signed-off-by: Greg Bomkamp --- .changeset/pretty-moons-drive.md | 2 +- packages/backend-common/config.d.ts | 13 ++++--- .../src/database/DatabaseManager.test.ts | 18 +++++----- .../src/database/DatabaseManager.ts | 35 +++++++++---------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.changeset/pretty-moons-drive.md b/.changeset/pretty-moons-drive.md index 3c3b7968c6..51dc69332e 100644 --- a/.changeset/pretty-moons-drive.md +++ b/.changeset/pretty-moons-drive.md @@ -2,4 +2,4 @@ '@backstage/backend-common': patch --- -Adding config prop `usePluginSchemas` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. +Adding config prop `pluginDivisionMode` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 4476a946cf..aafda9b7d4 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -70,15 +70,18 @@ export interface Config { */ ensureExists?: boolean; /** - * Whether plugins should use their own schemas instead of databases. If enabled, - * each plugin will create a schema in the configured database instance - * using the `pluginId` as its schema name. + * How plugins databases are managed/divided in the provided database instance. + * + * `database` -> Plugins are each given their own database to manage their schemas/tables. + * + * `schema` -> Plugins will be given their own schema (in the specified/default database) + * to manage their tables. * * NOTE: Currently only supported by the `pg` client. * - * @default false + * @default database */ - usePluginSchemas?: boolean; + pluginDivisionMode?: 'database' | 'schema'; /** Plugin specific database configuration and client override */ plugin?: { [pluginId: string]: { diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index e639f14ee8..f2fe855234 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -325,7 +325,7 @@ describe('DatabaseManager', () => { backend: { database: { client: 'pg', - usePluginSchemas: true, + pluginDivisionMode: 'schema', connection: { host: 'localhost', user: 'foo', @@ -360,7 +360,7 @@ describe('DatabaseManager', () => { backend: { database: { client: 'sqlite3', - usePluginSchemas: true, + pluginDivisionMode: 'schema', connection: { host: 'localhost', user: 'foo', @@ -385,13 +385,13 @@ describe('DatabaseManager', () => { expect(overrides).not.toHaveProperty('searchPath'); }); - it('plugin does not provide schema override if usePluginSchemas is not provided', async () => { + it('plugin does not provide schema override if pluginDivisionMode is set to database', async () => { const testManager = DatabaseManager.fromConfig( new ConfigReader({ backend: { database: { client: 'pg', - usePluginSchemas: false, + pluginDivisionMode: 'database', connection: 'some-file-path', }, }, @@ -407,7 +407,7 @@ describe('DatabaseManager', () => { expect(overrides).not.toHaveProperty('searchPath'); }); - it('plugin does not provide schema override if usePluginSchemas is false', async () => { + it('plugin does not provide schema override if pluginDivisionMode is not set', async () => { const testManager = DatabaseManager.fromConfig( new ConfigReader({ backend: { @@ -433,13 +433,13 @@ describe('DatabaseManager', () => { expect(overrides).not.toHaveProperty('searchPath'); }); - it('usePluginSchemas ensures that each plugin schema exists', async () => { + it('pluginDivisionMode ensures that each plugin schema exists', async () => { const testManager = DatabaseManager.fromConfig( new ConfigReader({ backend: { database: { client: 'pg', - usePluginSchemas: true, + pluginDivisionMode: 'schema', connection: { host: 'localhost', user: 'foo', @@ -459,13 +459,13 @@ describe('DatabaseManager', () => { expect(schemaName).toEqual('testdbname'); }); - it('usePluginSchemas allows connection overrides for plugins', async () => { + it('pluginDivisionMode allows connection overrides for plugins', async () => { const testManager = DatabaseManager.fromConfig( new ConfigReader({ backend: { database: { client: 'pg', - usePluginSchemas: true, + pluginDivisionMode: 'schema', connection: { host: 'localhost', user: 'foo', diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index efd779e4ed..6a2b6cff60 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -83,9 +83,9 @@ export class DatabaseManager { * * This method provides the effective database name which is determined using global * and plugin specific database config. If no explicit database name is configured - * and usePluginSchemas is not enabled, this method will provide a generated name - * which is the pluginId prefixed with 'backstage_plugin_'. If `usePluginSchemas` is - * enabled, it will fallback to using the default database for the knex instance. + * and `pluginDivisionMode` is not `schema`, this method will provide a generated name + * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is + * `schema`, it will fallback to using the default database for the knex instance. * * @param pluginId Lookup the database name for given plugin * @returns String representing the plugin's database name @@ -102,8 +102,8 @@ export class DatabaseManager { const databaseName = (connection as Knex.ConnectionConfig)?.database; - // usePluginSchemas enabled should use overridden databaseName if supplied or fallback to default knex database - if (this.getUsePluginSchemasConfig()) { + // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database + if (this.getPluginDivisionModeConfig() === 'schema') { return databaseName; } @@ -145,8 +145,8 @@ export class DatabaseManager { ); } - private getUsePluginSchemasConfig(): boolean { - return this.config.getOptionalBoolean('usePluginSchemas') ?? false; + private getPluginDivisionModeConfig(): string { + return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; } /** @@ -156,7 +156,7 @@ export class DatabaseManager { * has not been overridden, the global connection config will be included with plugin * specific config as the base. Values from the plugin connection take precedence over the * base. Base database name is omitted for all supported databases excluding SQLite unless - * `usePluginSchemas` is enabled. + * `pluginDivisionMode` is set to `schema`. */ private getConnectionConfig( pluginId: string, @@ -167,11 +167,11 @@ export class DatabaseManager { this.config.get('connection'), this.config.getString('client'), ); - // Databases cannot be shared unless the usePluginSchemas is enabled. The - // `database` property from the base connection is omitted unless usePluginSchemas - // is enabled. SQLite3's `filename` property is an exception as this is used as a + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The + // `database` property from the base connection is omitted unless `pluginDivisionMode` + // is set to `schema`. SQLite3's `filename` property is an exception as this is used as a // directory elsewhere so we preserve `filename`. - if (!this.getUsePluginSchemasConfig()) { + if (this.getPluginDivisionModeConfig() !== 'schema') { baseConnection = omit(baseConnection, 'database'); } @@ -221,12 +221,9 @@ export class DatabaseManager { * @returns Partial Knex.Config with database name override */ private getDatabaseOverrides(pluginId: string): Knex.Config { - const databaseNameOverride = this.getDatabaseName(pluginId); - return databaseNameOverride - ? createNameOverride( - this.getClientType(pluginId).client, - databaseNameOverride, - ) + const databaseName = this.getDatabaseName(pluginId); + return databaseName + ? createNameOverride(this.getClientType(pluginId).client, databaseName) : {}; } @@ -253,7 +250,7 @@ export class DatabaseManager { } let schemaOverrides; - if (this.getUsePluginSchemasConfig()) { + if (this.getPluginDivisionModeConfig() === 'schema') { try { schemaOverrides = this.getSchemaOverrides(pluginId); await ensureSchemaExists(pluginConfig, pluginId); From 69f06b7db5ee87498ae2dc9bba0047418b8875b3 Mon Sep 17 00:00:00 2001 From: Anthony Ikeda Date: Tue, 9 Nov 2021 11:58:58 -0800 Subject: [PATCH 026/139] Ran Prettier and reformatted the file Signed-off-by: Anthony Ikeda --- .../writing-custom-actions.md | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index d3dff2b4e8..76bf9a4d66 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -8,42 +8,9 @@ If you're wanting to extend the functionality of the Scaffolder, you can do so by writing custom actions which can be used along side our [built-in actions](./builtin-actions.md). - -> Note: When adding custom actions, the actions array will **replace the built-in actions too**, -<<<<<<< HEAD -> so if you want to have those as well as your new one. -> See below to include `builtInActions` during registration of your action. - -======= -> so if you want to have those as well as your new one, you'll need to do the following: - -```ts -import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; -import { ScmIntegrations } from '@backstage/integration'; - -const integrations = ScmIntegrations.fromConfig(config); - -const builtInActions = createBuiltinActions({ - containerRunner, - integrations, - config, - catalogClient, - reader, -}); - -const actions = [...builtInActions, createNewFileAction()]; - -return await createRouter({ - containerRunner, - logger, - config, - database, - catalogClient, - reader, - actions, -}); -``` ->>>>>>> a72c8bf39a (More consistent formatting of the Note block) +> Note: When adding custom actions, the actions array will **replace the +> built-in actions too**. To ensure you can continue to include he builtin +> actions, see below to include them during registration of your action. ### Writing your Custom Action From 45f3a67c4c174fa9cf7f65038ac7f374cccb5c6a Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 12 Nov 2021 12:39:31 +0100 Subject: [PATCH 027/139] add focused prop to both SearchBar and SearchBarBase components + storybook example Signed-off-by: Emma Indal --- .../components/SearchBar/SearchBar.stories.tsx | 17 +++++++++++++++++ .../src/components/SearchBar/SearchBar.tsx | 11 +++++++++-- .../src/components/SearchModal/SearchModal.tsx | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx index 2c990e512c..27a739006a 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx @@ -62,3 +62,20 @@ export const CustomPlaceholder = () => { ); }; + +export const Focused = () => { + return ( + + {/* @ts-ignore (defaultValue requires more than what is used here) */} + + + + + + + + + + + ); +}; diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index 8d493df5f8..5d503c5c44 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -30,9 +30,11 @@ type PresenterProps = { onSubmit?: () => void; className?: string; placeholder?: string; + focused?: boolean; }; export const SearchBarBase = ({ + focused, value, onChange, onSubmit, @@ -43,8 +45,10 @@ export const SearchBarBase = ({ const inputRef = useRef(); useEffect(() => { - inputRef?.current?.focus(); - }, []); + if (focused) { + inputRef?.current?.focus(); + } + }, [focused]); const onKeyDown = React.useCallback( (e: KeyboardEvent) => { @@ -93,12 +97,14 @@ export const SearchBarBase = ({ }; type Props = { + focused?: boolean; className?: string; debounceTime?: number; placeholder?: string; }; export const SearchBar = ({ + focused, className, debounceTime = 0, placeholder, @@ -120,6 +126,7 @@ export const SearchBar = ({ return ( { Date: Fri, 12 Nov 2021 12:43:03 +0100 Subject: [PATCH 028/139] update changeset Signed-off-by: Emma Indal --- .changeset/search-serious-hornets-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/search-serious-hornets-design.md b/.changeset/search-serious-hornets-design.md index 5fc47f00aa..e1c8656295 100644 --- a/.changeset/search-serious-hornets-design.md +++ b/.changeset/search-serious-hornets-design.md @@ -2,4 +2,4 @@ '@backstage/plugin-search': patch --- -Smaller UX improvements such as focused input field, decreased debounce value, closing modal on link click, terminology updates. +Smaller UX improvements to search components such as focused prop to SearchBar components, decreased debounce value and closing modal on link click of SearchModal, terminology updates of SearchResultPager. From a06242e5ccc2c54f3aac7255faa137ecea3ddaf9 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 12 Nov 2021 08:03:19 -0600 Subject: [PATCH 029/139] Refactored route names Signed-off-by: Andre Wanlin --- plugins/azure-devops/api-report.md | 8 +------- plugins/azure-devops/src/plugin.ts | 12 ++++++------ plugins/azure-devops/src/routes.ts | 9 +++++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index 578c2492a9..ab659534c1 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -7,17 +7,11 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { RouteRef } from '@backstage/core-plugin-api'; // Warning: (ae-missing-release-tag) "azureDevOpsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const azureDevOpsPlugin: BackstagePlugin< - { - entityContent: RouteRef; - }, - {} ->; +export const azureDevOpsPlugin: BackstagePlugin<{}, {}>; // Warning: (ae-missing-release-tag) "EntityAzurePipelinesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts index 55ec293360..7a5f80d0d3 100644 --- a/plugins/azure-devops/src/plugin.ts +++ b/plugins/azure-devops/src/plugin.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +import { + azurePipelinesEntityContentRouteRef, + azurePullRequestsEntityContentRouteRef, +} from './routes'; import { createApiFactory, createPlugin, @@ -26,7 +30,6 @@ import { AZURE_DEVOPS_ANNOTATION } from './constants'; import { AzureDevOpsClient } from './api/AzureDevOpsClient'; import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from './api/AzureDevOpsApi'; -import { azureDevOpsRouteRef } from './routes'; export const isAzureDevOpsAvailable = (entity: Entity) => Boolean(entity.metadata.annotations?.[AZURE_DEVOPS_ANNOTATION]); @@ -41,9 +44,6 @@ export const azureDevOpsPlugin = createPlugin({ new AzureDevOpsClient({ discoveryApi, identityApi }), }), ], - routes: { - entityContent: azureDevOpsRouteRef, - }, }); export const EntityAzurePipelinesContent = azureDevOpsPlugin.provide( @@ -53,7 +53,7 @@ export const EntityAzurePipelinesContent = azureDevOpsPlugin.provide( import('./components/EntityPageAzurePipelines').then( m => m.EntityPageAzurePipelines, ), - mountPoint: azureDevOpsRouteRef, + mountPoint: azurePipelinesEntityContentRouteRef, }), ); @@ -64,6 +64,6 @@ export const EntityAzurePullRequestsContent = azureDevOpsPlugin.provide( import('./components/EntityPageAzurePullRequests').then( m => m.EntityPageAzurePullRequests, ), - mountPoint: azureDevOpsRouteRef, + mountPoint: azurePullRequestsEntityContentRouteRef, }), ); diff --git a/plugins/azure-devops/src/routes.ts b/plugins/azure-devops/src/routes.ts index 81f4b48fda..7aaca416fa 100644 --- a/plugins/azure-devops/src/routes.ts +++ b/plugins/azure-devops/src/routes.ts @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createRouteRef } from '@backstage/core-plugin-api'; -export const azureDevOpsRouteRef = createRouteRef({ - title: 'azure-devops', +export const azurePipelinesEntityContentRouteRef = createRouteRef({ + title: 'Azure Pipelines Entity Content', +}); + +export const azurePullRequestsEntityContentRouteRef = createRouteRef({ + title: 'Azure Pull Requests Entity Content', }); From 48fe020c05788e6feb02b720a68f3fc0361649b7 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 12 Nov 2021 13:52:29 -0600 Subject: [PATCH 030/139] Updated to handle dates nicely with tests Signed-off-by: Andre Wanlin --- .../PullRequestTable/PullRequestTable.tsx | 5 +- .../src/utils/getDurationFromDates.test.ts | 58 ++++++++++++++----- .../src/utils/getDurationFromDates.ts | 10 ++-- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx index a898766394..90e5f55b04 100644 --- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx +++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx @@ -80,8 +80,9 @@ const columns: TableColumn[] = [ field: 'creationDate', width: 'auto', render: (row: Partial) => - DateTime.fromISO( - (row.creationDate ?? new Date()).toString(), + (row.creationDate + ? DateTime.fromISO(row.creationDate) + : DateTime.now() ).toRelative(), }, ]; diff --git a/plugins/azure-devops/src/utils/getDurationFromDates.test.ts b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts index 36763b7cba..fac0e917d0 100644 --- a/plugins/azure-devops/src/utils/getDurationFromDates.test.ts +++ b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts @@ -16,20 +16,52 @@ import { getDurationFromDates } from './getDurationFromDates'; -describe('getDurationFromDates with undefined startTime and valid finishTime', () => { - it('should return empty result', () => { - const finishTime = new Date('2021-10-15T11:00:00.0000000Z'); +describe('getDurationFromDates', () => { + beforeAll(() => { + jest.useFakeTimers('modern'); + jest.setSystemTime(new Date('2021-10-15T09:45:25.0000000Z')); + }); - const result = getDurationFromDates(undefined, finishTime); + afterAll(() => { + jest.useRealTimers(); + }); - expect(result).toEqual(''); - }); -}); - -describe('getDurationFromDates with undefined startTime and undefined finishTime', () => { - it('should return empty result', () => { - const result = getDurationFromDates(undefined, undefined); - - expect(result).toEqual(''); + describe('getDurationFromDates with valid startTime and valid finishTime', () => { + it('should return empty result', () => { + const startTime = '2021-10-15T09:30:00.0000000Z'; + const finishTime = '2021-10-15T11:00:00.0000000Z'; + + const result = getDurationFromDates(startTime, finishTime); + + expect(result).toEqual('1h, 30m'); + }); + }); + + describe('getDurationFromDates with valid startTime and undefined finishTime', () => { + it('should return empty result', () => { + const startTime = '2021-10-15T09:30:00.0000000Z'; + + const result = getDurationFromDates(startTime, undefined); + + expect(result).toEqual('15m, 25s'); + }); + }); + + describe('getDurationFromDates with undefined startTime and valid finishTime', () => { + it('should return empty result', () => { + const finishTime = '2021-10-15T11:00:00.0000000Z'; + + const result = getDurationFromDates(undefined, finishTime); + + expect(result).toEqual(''); + }); + }); + + describe('getDurationFromDates with undefined startTime and undefined finishTime', () => { + it('should return empty result', () => { + const result = getDurationFromDates(undefined, undefined); + + expect(result).toEqual(''); + }); }); }); diff --git a/plugins/azure-devops/src/utils/getDurationFromDates.ts b/plugins/azure-devops/src/utils/getDurationFromDates.ts index 5b1fb60219..789849f975 100644 --- a/plugins/azure-devops/src/utils/getDurationFromDates.ts +++ b/plugins/azure-devops/src/utils/getDurationFromDates.ts @@ -18,17 +18,15 @@ import { DateTime, Interval } from 'luxon'; import humanizeDuration from 'humanize-duration'; export const getDurationFromDates = ( - startTime?: Date, - finishTime?: Date, + startTime?: string, + finishTime?: string, ): string => { if (!startTime || (!startTime && !finishTime)) { return ''; } - const start = DateTime.fromISO(startTime.toString()); - const finish = finishTime - ? DateTime.fromISO(finishTime.toString()) - : DateTime.now(); + const start = DateTime.fromISO(startTime); + const finish = finishTime ? DateTime.fromISO(finishTime) : DateTime.now(); const formatted = Interval.fromDateTimes(start, finish) .toDuration() From ecdcbd08eebf11672f5fe748028f1a5e35e636fe Mon Sep 17 00:00:00 2001 From: Joon Park Date: Thu, 21 Oct 2021 17:54:20 +0100 Subject: [PATCH 031/139] Expose template metadata to custom action handler Currently, we only expose the template name to satisfy the requirement from the issue, but more can be added easily (as long as the type is expanded accordingly). Signed-off-by: Joon Park --- .changeset/yellow-falcons-whisper.md | 6 ++++ .../writing-custom-actions.md | 2 ++ plugins/scaffolder-backend/api-report.md | 10 +++++++ .../src/scaffolder/actions/types.ts | 3 ++ .../tasks/DefaultWorkflowRunner.test.ts | 24 +++++++++++++++ .../tasks/HandlebarsWorkflowRunner.ts | 7 +++++ .../tasks/LegacyWorkflowRunner.test.ts | 30 +++++++++++++++++++ .../tasks/NunjucksWorkflowRunner.ts | 7 +++++ .../src/scaffolder/tasks/index.ts | 1 + .../src/scaffolder/tasks/types.ts | 11 +++++++ .../scaffolder-backend/src/service/router.ts | 2 ++ 11 files changed, 103 insertions(+) create mode 100644 .changeset/yellow-falcons-whisper.md diff --git a/.changeset/yellow-falcons-whisper.md b/.changeset/yellow-falcons-whisper.md new file mode 100644 index 0000000000..9168b32c27 --- /dev/null +++ b/.changeset/yellow-falcons-whisper.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-common': patch +--- + +Expose template metadata to custom action handler in Scaffolder. diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 07c4891a6c..e578918cc0 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -91,6 +91,8 @@ argument. It looks like the following: - `createTemporaryDirectory` a function to call to give you a temporary directory somewhere on the runner so you can store some files there rather than polluting the `workspacePath` +- `ctx.metadata` - an object containing a `name` field, indicating the template + name. More metadata fields may be added later. ### Registering Custom Actions diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 3f9b77a3a3..4b27b938c9 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -41,6 +41,7 @@ export type ActionContext = { input: Input; output(name: string, value: JsonValue): void; createTemporaryDirectory(): Promise; + metadata?: TemplateMetadata; }; // Warning: (ae-missing-release-tag) "CatalogEntityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -432,6 +433,8 @@ export interface TaskSpecV1beta2 { // (undocumented) baseUrl?: string; // (undocumented) + metadata?: TemplateMetadata; + // (undocumented) output: { [name: string]: string; }; @@ -454,6 +457,8 @@ export interface TaskSpecV1beta3 { // (undocumented) baseUrl?: string; // (undocumented) + metadata?: TemplateMetadata; + // (undocumented) output: { [name: string]: JsonValue; }; @@ -558,4 +563,9 @@ export class TemplateActionRegistry { action: TemplateAction, ): void; } + +// @public +export type TemplateMetadata = { + name: string; +}; ``` diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/types.ts b/plugins/scaffolder-backend/src/scaffolder/actions/types.ts index ca3459d29c..52149be724 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/types.ts @@ -18,6 +18,7 @@ import { Logger } from 'winston'; import { Writable } from 'stream'; import { JsonValue, JsonObject } from '@backstage/types'; import { Schema } from 'jsonschema'; +import { TemplateMetadata } from '../tasks/types'; type PartialJsonObject = Partial; type PartialJsonValue = PartialJsonObject | JsonValue | undefined; @@ -44,6 +45,8 @@ export type ActionContext = { * Creates a temporary directory for use by the action, which is then cleaned up automatically. */ createTemporaryDirectory(): Promise; + + metadata?: TemplateMetadata; }; export type TemplateAction = { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts index d07ff29fae..1f7877791c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts @@ -146,6 +146,30 @@ describe('DefaultWorkflowRunner', () => { expect(fakeActionHandler).toHaveBeenCalledTimes(1); }); + + it('should pass metadata through', async () => { + const templateName = 'template name'; + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + parameters: {}, + output: {}, + steps: [ + { + id: 'test', + name: 'name', + action: 'jest-validated-action', + input: { foo: 1 }, + }, + ], + metadata: { name: templateName }, + }); + + await runner.execute(task); + + expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({ + name: templateName, + }); + }); }); describe('conditionals', () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/HandlebarsWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/HandlebarsWorkflowRunner.ts index 77d0a7be09..5582b53023 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/HandlebarsWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/HandlebarsWorkflowRunner.ts @@ -225,6 +225,12 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner { input: JSON.stringify(input, null, 2), }); + if (!task.spec.metadata) { + console.warn( + 'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.', + ); + } + await action.handler({ baseUrl: task.spec.baseUrl, logger: taskLogger, @@ -242,6 +248,7 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner { output(name: string, value: JsonValue) { stepOutputs[name] = value; }, + metadata: task.spec.metadata, }); // Remove all temporary directories that were created when executing the action diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts index 91e772dc4e..9b05c43831 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts @@ -28,6 +28,7 @@ describe('LegacyWorkflowRunner', () => { let runner: HandlebarsWorkflowRunner; const logger = getVoidLogger(); let actionRegistry = new TemplateActionRegistry(); + let fakeActionHandler: jest.Mock; const integrations = ScmIntegrations.fromConfig( new ConfigReader({ @@ -59,6 +60,11 @@ describe('LegacyWorkflowRunner', () => { ctx.output('badOutput', false); }, }); + fakeActionHandler = jest.fn(); + actionRegistry.register({ + id: 'test-metadata-action', + handler: fakeActionHandler, + }); runner = new HandlebarsWorkflowRunner({ actionRegistry, @@ -87,6 +93,30 @@ describe('LegacyWorkflowRunner', () => { ); }); + it('should pass metadata through', async () => { + const templateName = 'template name'; + const task = createMockTaskWithSpec({ + apiVersion: 'backstage.io/v1beta2', + steps: [ + { + id: 'test', + name: 'name', + action: 'test-metadata-action', + input: { foo: 1 }, + }, + ], + output: {}, + values: {}, + metadata: { name: templateName }, + }); + + await runner.execute(task); + + expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({ + name: templateName, + }); + }); + describe('templating', () => { it('should template the output', async () => { const task = createMockTaskWithSpec({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 587a93e1f2..c66d967103 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -228,6 +228,12 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const tmpDirs = new Array(); const stepOutput: { [outputName: string]: JsonValue } = {}; + if (!task.spec.metadata) { + console.warn( + 'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.', + ); + } + await action.handler({ baseUrl: task.spec.baseUrl, input, @@ -244,6 +250,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { output(name: string, value: JsonValue) { stepOutput[name] = value; }, + metadata: task.spec.metadata, }); // Remove all temporary directories that were created when executing the action diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts index 510c862dae..f563a08df6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts @@ -34,4 +34,5 @@ export type { TaskContext, TaskStore, DispatchResult, + TemplateMetadata, } from './types'; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 6cfd914bd2..b492f8ad2c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -69,6 +69,15 @@ export type SerializedTaskEvent = { createdAt: string; }; +/** + * TemplateMetadata + * + * @public + */ +export type TemplateMetadata = { + name: string; +}; + /** * TaskSpecV1beta2 * @@ -86,6 +95,7 @@ export interface TaskSpecV1beta2 { if?: string | boolean; }>; output: { [name: string]: string }; + metadata?: TemplateMetadata; } export interface TaskStep { @@ -107,6 +117,7 @@ export interface TaskSpecV1beta3 { parameters: JsonObject; steps: TaskStep[]; output: { [name: string]: JsonValue }; + metadata?: TemplateMetadata; } /** diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 2ae30593f2..b8f2968287 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -209,6 +209,7 @@ export async function createRouter( name: step.name ?? step.action, })), output: template.spec.output ?? {}, + metadata: { name: template.metadata?.name }, } : { apiVersion: template.apiVersion, @@ -220,6 +221,7 @@ export async function createRouter( name: step.name ?? step.action, })), output: template.spec.output ?? {}, + metadata: { name: template.metadata?.name }, }; } else { throw new InputError( From 4ebc9fd277fad6f14ada6d2570d2cdbf017778e6 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 20 Oct 2021 14:12:02 +0200 Subject: [PATCH 032/139] Create .backstage-version file Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 7 +++++++ packages/create-app/src/createApp.ts | 6 +++--- packages/create-app/src/index.ts | 2 +- packages/create-app/src/lib/tasks.ts | 7 ++++++- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 .changeset/healthy-kids-mate.md diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md new file mode 100644 index 0000000000..b9acfc24b0 --- /dev/null +++ b/.changeset/healthy-kids-mate.md @@ -0,0 +1,7 @@ +--- +'@backstage/create-app': patch +--- + +Add .bsversion file + +`@backstage/create-app` will create a new `.bsversion` file containing the version of `@backstage/create-app` that has been used for creating the application. diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 8823e6c858..d301792380 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -30,7 +30,7 @@ import { templatingTask, } from './lib/tasks'; -export default async (cmd: Command): Promise => { +export default async (cmd: Command, version: string): Promise => { /* eslint-disable-next-line no-restricted-syntax */ const paths = findPaths(__dirname); @@ -82,7 +82,7 @@ export default async (cmd: Command): Promise => { await checkPathExistsTask(appDir); Task.section('Preparing files'); - await templatingTask(templateDir, cmd.path, answers); + await templatingTask(templateDir, cmd.path, answers, version); } else { // Template to temporary location, and then move files @@ -93,7 +93,7 @@ export default async (cmd: Command): Promise => { await createTemporaryAppFolderTask(tempDir); Task.section('Preparing files'); - await templatingTask(templateDir, tempDir, answers); + await templatingTask(templateDir, tempDir, answers, version); Task.section('Moving to final location'); await moveAppTask(tempDir, appDir, answers.name); diff --git a/packages/create-app/src/index.ts b/packages/create-app/src/index.ts index 1f37be56d2..8a2da2fad8 100644 --- a/packages/create-app/src/index.ts +++ b/packages/create-app/src/index.ts @@ -38,7 +38,7 @@ const main = (argv: string[]) => { '--skip-install', 'Skip the install and builds steps after creating the app', ) - .action(createApp); + .action(cmd => createApp(cmd, version)); program.parse(argv); }; diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index 0b3ca63728..aefff2c9ee 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -19,7 +19,7 @@ import fs from 'fs-extra'; import handlebars from 'handlebars'; import ora from 'ora'; import recursive from 'recursive-readdir'; -import { basename, dirname, resolve as resolvePath } from 'path'; +import { basename, dirname, join, resolve as resolvePath } from 'path'; import { exec as execCb } from 'child_process'; import { packageVersions } from './versions'; import { promisify } from 'util'; @@ -79,6 +79,7 @@ export async function templatingTask( templateDir: string, destinationDir: string, context: any, + version: string, ) { const files = await recursive(templateDir).catch(error => { throw new Error(`Failed to read template directory: ${error.message}`); @@ -125,6 +126,10 @@ export async function templatingTask( }); } } + const bsVersionFileName = '.bsversion'; + await Task.forItem('creating', bsVersionFileName, () => + fs.writeFile(join(destinationDir, bsVersionFileName), version), + ); } /** From 085edcfc5fa3756eb2a6d6744ea1276ee4c770cb Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 20 Oct 2021 20:36:35 +0200 Subject: [PATCH 033/139] fix comments Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 4 ++-- packages/create-app/src/lib/tasks.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index b9acfc24b0..d94e380ee5 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -2,6 +2,6 @@ '@backstage/create-app': patch --- -Add .bsversion file +Add .backstage-version file -`@backstage/create-app` will create a new `.bsversion` file containing the version of `@backstage/create-app` that has been used for creating the application. +`@backstage/create-app` will create a new `.backstage-version` file containing the version of `@backstage/create-app` that has been used for creating the application. diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index aefff2c9ee..c8209435bf 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -126,9 +126,12 @@ export async function templatingTask( }); } } - const bsVersionFileName = '.bsversion'; - await Task.forItem('creating', bsVersionFileName, () => - fs.writeFile(join(destinationDir, bsVersionFileName), version), + const backstageVersionFileName = '.backstage-version'; + await Task.forItem('creating', backstageVersionFileName, () => + fs.writeFile( + join(destinationDir, backstageVersionFileName), + `${version}\n`, + ), ); } From b6b4d4b73fdefde1ddf3dc3acb1f204e677b7a60 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 10 Nov 2021 23:51:42 +0100 Subject: [PATCH 034/139] Bump .backstage.json version Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 4 +- packages/cli-common/api-report.md | 5 ++ packages/cli-common/src/index.ts | 2 +- packages/cli-common/src/paths.ts | 5 ++ .../cli/src/commands/versions/bump.test.ts | 65 ++++++++++++++++++- packages/cli/src/commands/versions/bump.ts | 40 ++++++++++++ packages/create-app/src/lib/tasks.ts | 8 +-- 7 files changed, 121 insertions(+), 8 deletions(-) diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index d94e380ee5..3ed8172199 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -1,7 +1,9 @@ --- +'@backstage/cli': patch +'@backstage/cli-common': patch '@backstage/create-app': patch --- Add .backstage-version file -`@backstage/create-app` will create a new `.backstage-version` file containing the version of `@backstage/create-app` that has been used for creating the application. +`@backstage/create-app` will create a new `.backstage.json` file containing the version of `@backstage/create-app` used for creating the application. diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md index 4b61cfedb8..e100b40cc2 100644 --- a/packages/cli-common/api-report.md +++ b/packages/cli-common/api-report.md @@ -3,6 +3,11 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +// Warning: (ae-missing-release-tag) "BACKSTAGE_JSON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const BACKSTAGE_JSON = '.backstage.json'; + // @public export function findPaths(searchDir: string): Paths; diff --git a/packages/cli-common/src/index.ts b/packages/cli-common/src/index.ts index e7cda19514..8809764b43 100644 --- a/packages/cli-common/src/index.ts +++ b/packages/cli-common/src/index.ts @@ -20,6 +20,6 @@ * @packageDocumentation */ -export { findPaths } from './paths'; +export { findPaths, BACKSTAGE_JSON } from './paths'; export { isChildPath } from './isChildPath'; export type { Paths, ResolveFunc } from './paths'; diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index c9f486752f..2f46fee27a 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -167,3 +167,8 @@ export function findPaths(searchDir: string): Paths { resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths), }; } + +/** + * The name of the backstage's config file + */ +export const BACKSTAGE_JSON = '.backstage.json'; diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 96bd1ab6fd..6759928458 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -20,7 +20,7 @@ import { resolve as resolvePath } from 'path'; import { paths } from '../../lib/paths'; import { mapDependencies } from '../../lib/versioning'; import * as runObj from '../../lib/run'; -import bump from './bump'; +import bump, { bumpBackstageJsonVersion } from './bump'; import { withLogCollector } from '@backstage/test-utils'; // Remove log coloring to simplify log matching @@ -141,7 +141,7 @@ describe('bump', () => { 'Version bump complete!', ]); - expect(runObj.runPlain).toHaveBeenCalledTimes(3); + expect(runObj.runPlain).toHaveBeenCalledTimes(4); expect(runObj.runPlain).toHaveBeenCalledWith( 'yarn', 'info', @@ -245,3 +245,64 @@ describe('bump', () => { }); }); }); + +describe('bumpBackstageJsonVersion', () => { + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + + it('should bump version in .backstage.json', async () => { + mockFs({ + '/.backstage.json': JSON.stringify({ version: '0.0.1' }), + }); + paths.targetDir = '/'; + const latest = '1.4.1'; + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...path) => resolvePath('/', ...path)); + jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => + JSON.stringify({ + type: 'inspect', + data: { + name, + 'dist-tags': { + latest, + }, + }, + }), + ); + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + + await bumpBackstageJsonVersion(); + + const json = await fs.readJson('/.backstage.json'); + expect(json).toEqual({ version: '1.4.1' }); + }); + + it("should create .backstage.json if doesn't exist", async () => { + mockFs({}); + paths.targetDir = '/'; + const latest = '1.4.1'; + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...path) => resolvePath('/', ...path)); + jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => + JSON.stringify({ + type: 'inspect', + data: { + name, + 'dist-tags': { + latest, + }, + }, + }), + ); + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + + await bumpBackstageJsonVersion(); + + const json = await fs.readJson('/.backstage.json'); + expect(json).toEqual({ version: '1.4.1' }); + }); +}); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 7b967839b2..0e37895cad 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -27,6 +27,7 @@ import { Lockfile, } from '../../lib/versioning'; import { includedFilter, forbiddenDuplicatesFilter } from './lint'; +import { BACKSTAGE_JSON } from '@backstage/cli-common'; const DEP_TYPES = [ 'dependencies', @@ -182,6 +183,10 @@ export default async () => { await fs.writeJson(pkgPath, pkgJson, { spaces: 2 }); }); + console.log(); + + await bumpBackstageJsonVersion(); + console.log(); console.log( `Running ${chalk.blue('yarn install')} to install new versions`, @@ -271,6 +276,41 @@ function createVersionFinder() { }; } +export async function bumpBackstageJsonVersion() { + const backstageJsonPath = paths.resolveTargetRoot(BACKSTAGE_JSON); + const backstageJson = await fs.readJSON(backstageJsonPath).catch(e => { + if (e.code === 'ENOENT') { + // gracefully continue in case the file doesn't exist + return; + } + throw e; + }); + + const info = await fetchPackageInfo('@backstage/create-app'); + const { latest } = info['dist-tags']; + + if (backstageJson?.version === latest) { + return; + } + + console.log( + chalk.yellow( + typeof backstageJson === 'undefined' + ? 'Creating .backstage.json' + : 'Bumping version in .backstage.json', + ), + ); + + await fs.writeJson( + backstageJsonPath, + { ...backstageJson, version: latest }, + { + spaces: 2, + encoding: 'utf8', + }, + ); +} + async function workerThreads( count: number, items: IterableIterator, diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index c8209435bf..86e178de52 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { BACKSTAGE_JSON } from '@backstage/cli-common'; import chalk from 'chalk'; import fs from 'fs-extra'; import handlebars from 'handlebars'; @@ -126,11 +127,10 @@ export async function templatingTask( }); } } - const backstageVersionFileName = '.backstage-version'; - await Task.forItem('creating', backstageVersionFileName, () => + await Task.forItem('creating', BACKSTAGE_JSON, () => fs.writeFile( - join(destinationDir, backstageVersionFileName), - `${version}\n`, + join(destinationDir, BACKSTAGE_JSON), + `{\n "version": ${JSON.stringify(version)}\n}\n`, ), ); } From 677bfc2dd00ef382abb8025d3e3632aaa08615f8 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 15 Nov 2021 16:32:46 +0100 Subject: [PATCH 035/139] fix comments Signed-off-by: Vincenzo Scamporlino --- .changeset/cyan-bottles-hug.md | 8 ++++++++ .changeset/healthy-kids-mate.md | 10 ++++++---- packages/cli-common/api-report.md | 4 +--- packages/cli-common/src/paths.ts | 4 +++- packages/cli/src/commands/versions/bump.test.ts | 10 +++++----- packages/cli/src/commands/versions/bump.ts | 4 ++-- packages/create-app/src/createApp.test.ts | 6 +++--- packages/create-app/src/lib/tasks.test.ts | 5 ++++- 8 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 .changeset/cyan-bottles-hug.md diff --git a/.changeset/cyan-bottles-hug.md b/.changeset/cyan-bottles-hug.md new file mode 100644 index 0000000000..7c2083c4ff --- /dev/null +++ b/.changeset/cyan-bottles-hug.md @@ -0,0 +1,8 @@ +--- +'@backstage/cli': patch +'@backstage/cli-common': patch +--- + +Keep backstage.json in sync + +The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present. diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index 3ed8172199..10e8222a15 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -1,9 +1,11 @@ --- -'@backstage/cli': patch -'@backstage/cli-common': patch '@backstage/create-app': patch --- -Add .backstage-version file +Create backstage.json file -`@backstage/create-app` will create a new `.backstage.json` file containing the version of `@backstage/create-app` used for creating the application. +`@backstage/create-app` will create a new `backstage.json` file. At this point, the file will contain a `version` property, representing the version of `@backstage/create-app` used for creating the application. If the backstage's application has been bootstrapped using an older version of `@backstage/create-app`, the `backstage.json` file can be created and kept in sync, together with all the changes of the latest version of backstage, by running the following script: + +```bash +yarn backstage-cli versions:bump +``` diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md index e100b40cc2..34f56c3f19 100644 --- a/packages/cli-common/api-report.md +++ b/packages/cli-common/api-report.md @@ -3,10 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -// Warning: (ae-missing-release-tag) "BACKSTAGE_JSON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public -export const BACKSTAGE_JSON = '.backstage.json'; +export const BACKSTAGE_JSON = 'backstage.json'; // @public export function findPaths(searchDir: string): Paths; diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 2f46fee27a..2b1ca34ca5 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -170,5 +170,7 @@ export function findPaths(searchDir: string): Paths { /** * The name of the backstage's config file + * + * @public */ -export const BACKSTAGE_JSON = '.backstage.json'; +export const BACKSTAGE_JSON = 'backstage.json'; diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 6759928458..bc4049f1f4 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -252,9 +252,9 @@ describe('bumpBackstageJsonVersion', () => { jest.resetAllMocks(); }); - it('should bump version in .backstage.json', async () => { + it('should bump version in backstage.json', async () => { mockFs({ - '/.backstage.json': JSON.stringify({ version: '0.0.1' }), + '/backstage.json': JSON.stringify({ version: '0.0.1' }), }); paths.targetDir = '/'; const latest = '1.4.1'; @@ -276,11 +276,11 @@ describe('bumpBackstageJsonVersion', () => { await bumpBackstageJsonVersion(); - const json = await fs.readJson('/.backstage.json'); + const json = await fs.readJson('/backstage.json'); expect(json).toEqual({ version: '1.4.1' }); }); - it("should create .backstage.json if doesn't exist", async () => { + it("should create backstage.json if doesn't exist", async () => { mockFs({}); paths.targetDir = '/'; const latest = '1.4.1'; @@ -302,7 +302,7 @@ describe('bumpBackstageJsonVersion', () => { await bumpBackstageJsonVersion(); - const json = await fs.readJson('/.backstage.json'); + const json = await fs.readJson('/backstage.json'); expect(json).toEqual({ version: '1.4.1' }); }); }); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 0e37895cad..3898c1654a 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -296,8 +296,8 @@ export async function bumpBackstageJsonVersion() { console.log( chalk.yellow( typeof backstageJson === 'undefined' - ? 'Creating .backstage.json' - : 'Bumping version in .backstage.json', + ? `Creating ${BACKSTAGE_JSON}` + : `Bumping version in ${BACKSTAGE_JSON}`, ), ); diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index 7b3b8f124c..5e54fac31e 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -59,7 +59,7 @@ describe('command entrypoint', () => { test('should call expected tasks with no path option', async () => { const cmd = {} as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(checkAppExistsMock).toHaveBeenCalled(); expect(createTemporaryAppFolderMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); @@ -69,7 +69,7 @@ describe('command entrypoint', () => { it('should call expected tasks with path option', async () => { const cmd = { path: 'myDirectory' } as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(checkPathExistsMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); expect(buildAppMock).toHaveBeenCalled(); @@ -77,7 +77,7 @@ describe('command entrypoint', () => { it('should not call `buildAppTask` when `skipInstall` is supplied', async () => { const cmd = { skipInstall: true } as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(buildAppMock).not.toHaveBeenCalled(); }); }); diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index d69526cb49..24df97c694 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -195,9 +195,12 @@ describe('templatingTask', () => { name: 'SuperCoolBackstageInstance', dbTypeSqlite: true, }; - await templatingTask(templateDir, destinationDir, context); + await templatingTask(templateDir, destinationDir, context, '1.0.0'); expect(fs.existsSync('templatedApp/package.json')).toBe(true); expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true); + await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual({ + version: '1.0.0', + }); // catalog was populated with `context.name` expect( fs.readFileSync('templatedApp/catalog-info.yaml', 'utf-8'), From f091863f038b7606033f35216ef45834c75b956f Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Tue, 2 Nov 2021 17:47:09 +0000 Subject: [PATCH 036/139] Add an entity fact retriever Signed-off-by: Iain Billett --- .../entityFactRetriever.test.ts | 111 ++++++++++++++++++ .../factRetrievers/entityFactRetriever.ts | 92 +++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts create mode 100644 plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts new file mode 100644 index 0000000000..c7c8ecf77a --- /dev/null +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts @@ -0,0 +1,111 @@ +/* + * 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 { entityFactRetriever } from './entityFactRetriever'; +import { Entity } from '@backstage/catalog-model'; +import { + PluginEndpointDiscovery, + getVoidLogger, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { CatalogListResponse } from '@backstage/catalog-client'; + +const getEntitiesMock = jest.fn(); +jest.mock('@backstage/catalog-client', () => { + return { + CatalogClient: jest + .fn() + .mockImplementation(() => ({ getEntities: getEntitiesMock })), + }; +}); +const discovery: jest.Mocked = { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), +}; + +const defaultEntityListResponse: CatalogListResponse = { + items: [ + { + apiVersion: 'backstage.io/v1beta1', + metadata: { name: 'service-with-owner' }, + kind: 'Component', + spec: { + type: 'service', + lifecycle: 'test', + owner: 'group:team-a', + }, + relations: [ + { + type: 'ownedBy', + target: { name: 'team-a', kind: 'group', namespace: 'default' }, + }, + ], + }, + { + apiVersion: 'backstage.io/v1beta1', + metadata: { + name: 'service-without-owner', + }, + kind: 'Component', + spec: { + type: 'service', + lifecycle: 'test', + }, + }, + ], +}; + +const handlerContext = { + discovery, + logger: getVoidLogger(), + config: ConfigReader.fromConfigs([]), +}; + +describe('entityFactRetriever', () => { + beforeEach(() => { + getEntitiesMock.mockResolvedValue(defaultEntityListResponse); + }); + afterEach(() => { + getEntitiesMock.mockClear(); + }); + + describe('hasSpecWithOwner', () => { + describe('where the entity has an owner in the spec', () => { + it('returns true for hasSpecWithOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-owner'), + ).toMatchObject({ + facts: { + hasSpecWithOwner: true, + }, + }); + }); + }); + describe('where the entity does not have an owner in the spec', () => { + it('returns false for hasSpecWithOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-without-owner'), + ).toMatchObject({ + facts: { + hasSpecWithOwner: false, + }, + }); + }); + }); + }); +}); diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts new file mode 100644 index 0000000000..8bc670b873 --- /dev/null +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts @@ -0,0 +1,92 @@ +/* + * 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 { FactRetrieverContext } from '@backstage/plugin-tech-insights-node'; +import { CatalogClient } from '@backstage/catalog-client'; +import { RELATION_OWNED_BY } from '@backstage/catalog-model'; + +const applicableEntityKinds = [ + 'component', + 'api', + 'template', + 'domain', + 'resource', + 'system', +]; + +const entityKindFilter = { field: 'kind', values: applicableEntityKinds }; + +export const entityFactRetriever = { + id: 'entityRetriever', + version: '0.0.1', + entityFilter: [ + { + field: 'kind', + values: applicableEntityKinds, + }, + ], + schema: { + hasSpecWithOwner: { + type: 'boolean', + description: 'The spec.owner field is set', + }, + hasSpecWithGroupOwner: { + type: 'boolean', + description: 'The spec.owner field is set and refers to a group', + }, + hasOwnedByRelation: { + type: 'boolean', + description: 'The entity has an owned_by relation', + }, + hasOwnedByRelationWithGroupTarget: { + type: 'boolean', + description: 'The entity has an owned_by relation which targets a group', + }, + }, + handler: async ({ discovery }: FactRetrieverContext) => { + const catalogClient = new CatalogClient({ + discoveryApi: discovery, + }); + const entities = await catalogClient.getEntities({ + filter: entityKindFilter, + }); + + return entities.items.map(it => { + return { + entity: { + namespace: it.metadata.namespace!!, + kind: it.kind, + name: it.metadata.name, + }, + facts: { + hasSpecWithOwner: Boolean(it.spec?.owner), + hasSpecWithGroupOwner: Boolean(it.spec?.owner && it.spec?.owner), + hasOwnedByRelation: Boolean( + it.relations && + it.relations.find(({ type }) => type === RELATION_OWNED_BY), + ), + hasOwnedByRelationWithGroupTarget: Boolean( + it.relations && + it.relations.find( + ({ type, target }) => + type === RELATION_OWNED_BY && target.kind === 'group', + ), + ), + }, + }; + }); + }, +}; From 23195a4494fdb37eebf2796ea16f1ddb330227cb Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Tue, 2 Nov 2021 18:26:36 +0000 Subject: [PATCH 037/139] Add some more test cases Signed-off-by: Iain Billett --- .../entityFactRetriever.test.ts | 55 ++++++++++++++++++- .../factRetrievers/entityFactRetriever.ts | 8 ++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts index c7c8ecf77a..fe4cd18645 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts @@ -15,7 +15,7 @@ */ import { entityFactRetriever } from './entityFactRetriever'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, getVoidLogger, @@ -49,7 +49,7 @@ const defaultEntityListResponse: CatalogListResponse = { }, relations: [ { - type: 'ownedBy', + type: RELATION_OWNED_BY, target: { name: 'team-a', kind: 'group', namespace: 'default' }, }, ], @@ -63,6 +63,19 @@ const defaultEntityListResponse: CatalogListResponse = { spec: { type: 'service', lifecycle: 'test', + owner: '', + }, + }, + { + apiVersion: 'backstage.io/v1beta1', + metadata: { + name: 'service-with-user-owner', + }, + kind: 'Component', + spec: { + type: 'service', + lifecycle: 'test', + owner: 'user:my-user', }, }, ], @@ -108,4 +121,42 @@ describe('entityFactRetriever', () => { }); }); }); + describe('hasSpecWithGroupOwner', () => { + describe('where the entity has an group as owner in the spec', () => { + it('returns true for hasSpecWithGroupOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-owner'), + ).toMatchObject({ + facts: { + hasSpecWithGroupOwner: true, + }, + }); + }); + }); + describe('where the entity has a user as owner in the spec', () => { + it('returns false for hasSpecWithGroupOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-user-owner'), + ).toMatchObject({ + facts: { + hasSpecWithGroupOwner: false, + }, + }); + }); + }); + describe('where the entity does not have an owner in the spec', () => { + it('returns false for hasSpecWithGroupOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-without-owner'), + ).toMatchObject({ + facts: { + hasSpecWithGroupOwner: false, + }, + }); + }); + }); + }); }); diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts index 8bc670b873..1488cf71ca 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts @@ -16,7 +16,7 @@ import { FactRetrieverContext } from '@backstage/plugin-tech-insights-node'; import { CatalogClient } from '@backstage/catalog-client'; -import { RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; const applicableEntityKinds = [ 'component', @@ -64,7 +64,7 @@ export const entityFactRetriever = { filter: entityKindFilter, }); - return entities.items.map(it => { + return entities.items.map((it: Entity) => { return { entity: { namespace: it.metadata.namespace!!, @@ -73,7 +73,9 @@ export const entityFactRetriever = { }, facts: { hasSpecWithOwner: Boolean(it.spec?.owner), - hasSpecWithGroupOwner: Boolean(it.spec?.owner && it.spec?.owner), + hasSpecWithGroupOwner: Boolean( + it.spec?.owner && (it.spec?.owner as string).startsWith('group:'), + ), hasOwnedByRelation: Boolean( it.relations && it.relations.find(({ type }) => type === RELATION_OWNED_BY), From 9d2d1bd00cedafc52db4c90dbf72c36f9d2aca5e Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 8 Nov 2021 12:42:21 +0000 Subject: [PATCH 038/139] Add some more test cases Signed-off-by: Iain Billett --- .../entityFactRetriever.test.ts | 125 +++++++++++++++--- .../factRetrievers/entityFactRetriever.ts | 51 ++----- 2 files changed, 117 insertions(+), 59 deletions(-) diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts index fe4cd18645..62769288fd 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts @@ -40,7 +40,11 @@ const defaultEntityListResponse: CatalogListResponse = { items: [ { apiVersion: 'backstage.io/v1beta1', - metadata: { name: 'service-with-owner' }, + metadata: { + name: 'service-with-owner', + description: 'service with an owner', + tags: ['a-tag'], + }, kind: 'Component', spec: { type: 'service', @@ -57,7 +61,9 @@ const defaultEntityListResponse: CatalogListResponse = { { apiVersion: 'backstage.io/v1beta1', metadata: { - name: 'service-without-owner', + name: 'service-with-incomplete-data', + description: '', + tags: [], }, kind: 'Component', spec: { @@ -78,6 +84,16 @@ const defaultEntityListResponse: CatalogListResponse = { owner: 'user:my-user', }, }, + { + apiVersion: 'backstage.io/v1beta1', + metadata: { + name: 'user-a', + }, + kind: 'User', + spec: { + memberOf: 'group-a', + }, + }, ], }; @@ -95,65 +111,138 @@ describe('entityFactRetriever', () => { getEntitiesMock.mockClear(); }); - describe('hasSpecWithOwner', () => { + describe('hasOwner', () => { describe('where the entity has an owner in the spec', () => { - it('returns true for hasSpecWithOwner', async () => { + it('returns true for hasOwner', async () => { const facts = await entityFactRetriever.handler(handlerContext); expect( facts.find(it => it.entity.name === 'service-with-owner'), ).toMatchObject({ facts: { - hasSpecWithOwner: true, + hasOwner: true, }, }); }); }); - describe('where the entity does not have an owner in the spec', () => { - it('returns false for hasSpecWithOwner', async () => { + describe('where the entity has an empty value for owner in the spec', () => { + it('returns false for hasOwner', async () => { const facts = await entityFactRetriever.handler(handlerContext); expect( - facts.find(it => it.entity.name === 'service-without-owner'), + facts.find(it => it.entity.name === 'service-with-incomplete-data'), ).toMatchObject({ facts: { - hasSpecWithOwner: false, + hasOwner: false, + }, + }); + }); + }); + + describe('where the entity doesn not have an owner in the spec', () => { + it('returns false for hasOwner', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect(facts.find(it => it.entity.name === 'user-a')).toMatchObject({ + facts: { + hasOwner: false, }, }); }); }); }); - describe('hasSpecWithGroupOwner', () => { + describe('hasGroupOwner', () => { describe('where the entity has an group as owner in the spec', () => { - it('returns true for hasSpecWithGroupOwner', async () => { + it('returns true for hasGroupOwner', async () => { const facts = await entityFactRetriever.handler(handlerContext); expect( facts.find(it => it.entity.name === 'service-with-owner'), ).toMatchObject({ facts: { - hasSpecWithGroupOwner: true, + hasGroupOwner: true, }, }); }); }); describe('where the entity has a user as owner in the spec', () => { - it('returns false for hasSpecWithGroupOwner', async () => { + it('returns false for hasGroupOwner', async () => { const facts = await entityFactRetriever.handler(handlerContext); expect( facts.find(it => it.entity.name === 'service-with-user-owner'), ).toMatchObject({ facts: { - hasSpecWithGroupOwner: false, + hasGroupOwner: false, }, }); }); }); - describe('where the entity does not have an owner in the spec', () => { - it('returns false for hasSpecWithGroupOwner', async () => { + describe('where the entity has an empty value for owner in the spec', () => { + it('returns false for hasGroupOwner', async () => { const facts = await entityFactRetriever.handler(handlerContext); expect( - facts.find(it => it.entity.name === 'service-without-owner'), + facts.find(it => it.entity.name === 'service-with-incomplete-data'), ).toMatchObject({ facts: { - hasSpecWithGroupOwner: false, + hasGroupOwner: false, + }, + }); + }); + }); + }); + describe('hasDescription', () => { + describe('where the entity has a description', () => { + it('returns true for hasDescription', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-owner'), + ).toMatchObject({ + facts: { + hasDescription: true, + }, + }); + }); + }); + describe('where the entity does not have a description', () => { + it('returns false for hasDescription', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect(facts.find(it => it.entity.name === 'user-a')).toMatchObject({ + facts: { + hasDescription: false, + }, + }); + }); + }); + describe('where the entity has an empty value for description', () => { + it('returns false for hasDescription', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-incomplete-data'), + ).toMatchObject({ + facts: { + hasDescription: false, + }, + }); + }); + }); + }); + describe('hasTags', () => { + describe('where the entity has tags', () => { + it('returns true for hasTags', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-owner'), + ).toMatchObject({ + facts: { + hasTags: true, + }, + }); + }); + }); + describe('where the entity has no tags', () => { + it('returns false for hasTags', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-incomplete-data'), + ).toMatchObject({ + facts: { + hasTags: false, }, }); }); diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts index 1488cf71ca..7d6a3987e0 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts @@ -16,53 +16,31 @@ import { FactRetrieverContext } from '@backstage/plugin-tech-insights-node'; import { CatalogClient } from '@backstage/catalog-client'; -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; - -const applicableEntityKinds = [ - 'component', - 'api', - 'template', - 'domain', - 'resource', - 'system', -]; - -const entityKindFilter = { field: 'kind', values: applicableEntityKinds }; +import { Entity } from '@backstage/catalog-model'; +import isEmpty from 'lodash/isEmpty'; export const entityFactRetriever = { id: 'entityRetriever', version: '0.0.1', - entityFilter: [ - { - field: 'kind', - values: applicableEntityKinds, - }, - ], schema: { - hasSpecWithOwner: { + hasOwner: { type: 'boolean', description: 'The spec.owner field is set', }, - hasSpecWithGroupOwner: { + hasGroupOwner: { type: 'boolean', description: 'The spec.owner field is set and refers to a group', }, - hasOwnedByRelation: { + hasDescription: { type: 'boolean', description: 'The entity has an owned_by relation', }, - hasOwnedByRelationWithGroupTarget: { - type: 'boolean', - description: 'The entity has an owned_by relation which targets a group', - }, }, handler: async ({ discovery }: FactRetrieverContext) => { const catalogClient = new CatalogClient({ discoveryApi: discovery, }); - const entities = await catalogClient.getEntities({ - filter: entityKindFilter, - }); + const entities = await catalogClient.getEntities(); return entities.items.map((it: Entity) => { return { @@ -72,21 +50,12 @@ export const entityFactRetriever = { name: it.metadata.name, }, facts: { - hasSpecWithOwner: Boolean(it.spec?.owner), - hasSpecWithGroupOwner: Boolean( + hasOwner: Boolean(it.spec?.owner), + hasGroupOwner: Boolean( it.spec?.owner && (it.spec?.owner as string).startsWith('group:'), ), - hasOwnedByRelation: Boolean( - it.relations && - it.relations.find(({ type }) => type === RELATION_OWNED_BY), - ), - hasOwnedByRelationWithGroupTarget: Boolean( - it.relations && - it.relations.find( - ({ type, target }) => - type === RELATION_OWNED_BY && target.kind === 'group', - ), - ), + hasDescription: Boolean(it.metadata?.description), + hasTags: !isEmpty(it.metadata?.tags), }, }; }); From 2d4593254fcfe625001ad025bf9250aaa0572d16 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Fri, 12 Nov 2021 10:20:25 +0000 Subject: [PATCH 039/139] Dynamic annotations Signed-off-by: Iain Billett --- .../entityFactRetriever.test.ts | 38 +++++++++++++++- .../factRetrievers/entityFactRetriever.ts | 45 ++++++++++++++----- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts index 62769288fd..a1741c22ee 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { entityFactRetriever } from './entityFactRetriever'; +import { createEntityFactRetriever } from './entityFactRetriever'; import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, @@ -44,6 +44,9 @@ const defaultEntityListResponse: CatalogListResponse = { name: 'service-with-owner', description: 'service with an owner', tags: ['a-tag'], + annotations: { + 'backstage.io/techdocs-ref': 'dir:.', + }, }, kind: 'Component', spec: { @@ -103,6 +106,10 @@ const handlerContext = { config: ConfigReader.fromConfigs([]), }; +const entityFactRetriever = createEntityFactRetriever({ + annotations: ['backstage.io/techdocs-ref'], +}); + describe('entityFactRetriever', () => { beforeEach(() => { getEntitiesMock.mockResolvedValue(defaultEntityListResponse); @@ -248,4 +255,33 @@ describe('entityFactRetriever', () => { }); }); }); + + describe('dynamic annotation facts', () => { + describe('where the retriever is configured to check for the techdocs annotation', () => { + describe('where the entity has the techdocs annotation', () => { + it('returns true for hasAnnotationBackstageIoTechdocsRef', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-owner'), + ).toMatchObject({ + facts: { + hasAnnotationBackstageIoTechdocsRef: true, + }, + }); + }); + }); + describe('where the entity is missing the techdocs annotation', () => { + it('returns false for hasAnnotationBackstageIoTechdocsRef', async () => { + const facts = await entityFactRetriever.handler(handlerContext); + expect( + facts.find(it => it.entity.name === 'service-with-incomplete-data'), + ).toMatchObject({ + facts: { + hasAnnotationBackstageIoTechdocsRef: false, + }, + }); + }); + }); + }); + }); }); diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts index 7d6a3987e0..66501ab1ec 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts @@ -18,8 +18,14 @@ import { FactRetrieverContext } from '@backstage/plugin-tech-insights-node'; import { CatalogClient } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import isEmpty from 'lodash/isEmpty'; +import camelCase from 'lodash/camelCase'; +import { get } from 'lodash'; -export const entityFactRetriever = { +export const createEntityFactRetriever = ({ + annotations = [], +}: { + annotations?: string[]; +}) => ({ id: 'entityRetriever', version: '0.0.1', schema: { @@ -35,6 +41,15 @@ export const entityFactRetriever = { type: 'boolean', description: 'The entity has an owned_by relation', }, + ...annotations.reduce((acc: object, it: string) => { + return { + ...acc, + [camelCase(`hasAnnotation-${it}`)]: { + type: 'boolean', + description: `The entity has the annotation: ${it} `, + }, + }; + }, {}), }, handler: async ({ discovery }: FactRetrieverContext) => { const catalogClient = new CatalogClient({ @@ -42,22 +57,32 @@ export const entityFactRetriever = { }); const entities = await catalogClient.getEntities(); - return entities.items.map((it: Entity) => { + return entities.items.map((entity: Entity) => { return { entity: { - namespace: it.metadata.namespace!!, - kind: it.kind, - name: it.metadata.name, + namespace: entity.metadata.namespace!!, + kind: entity.kind, + name: entity.metadata.name, }, facts: { - hasOwner: Boolean(it.spec?.owner), + hasOwner: Boolean(entity.spec?.owner), hasGroupOwner: Boolean( - it.spec?.owner && (it.spec?.owner as string).startsWith('group:'), + entity.spec?.owner && + (entity.spec?.owner as string).startsWith('group:'), + ), + hasDescription: Boolean(entity.metadata?.description), + hasTags: !isEmpty(entity.metadata?.tags), + ...annotations.reduce( + (acc: object, annotation: string) => ({ + ...acc, + [camelCase(`hasAnnotation-${annotation}`)]: Boolean( + get(entity, ['metadata', 'annotations', annotation]), + ), + }), + {}, ), - hasDescription: Boolean(it.metadata?.description), - hasTags: !isEmpty(it.metadata?.tags), }, }; }); }, -}; +}); From 5b31874a768921ee5c4407957240fbf3333c8191 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 15 Nov 2021 12:20:01 +0000 Subject: [PATCH 040/139] Add hasTags schema Signed-off-by: Iain Billett --- .../src/service/fact/factRetrievers/entityFactRetriever.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts index 66501ab1ec..1442d4d929 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts @@ -41,6 +41,10 @@ export const createEntityFactRetriever = ({ type: 'boolean', description: 'The entity has an owned_by relation', }, + hasTags: { + type: 'boolean', + description: 'The entity has tags in metadata', + }, ...annotations.reduce((acc: object, it: string) => { return { ...acc, From d4a3dbb75b448137cda3578b7c90e99f4e3b2f89 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 15 Nov 2021 18:27:48 +0000 Subject: [PATCH 041/139] Expose catalogFactRetriever & configure in backend Signed-off-by: Iain Billett --- packages/backend/src/plugins/techInsights.ts | 41 +++---------------- plugins/tech-insights-backend/src/index.ts | 1 + ...r.test.ts => catalogFactRetriever.test.ts} | 6 +-- ...ctRetriever.ts => catalogFactRetriever.ts} | 25 ++++++----- .../src/service/fact/factRetrievers/index.ts | 16 ++++++++ 5 files changed, 40 insertions(+), 49 deletions(-) rename plugins/tech-insights-backend/src/service/fact/factRetrievers/{entityFactRetriever.test.ts => catalogFactRetriever.test.ts} (98%) rename plugins/tech-insights-backend/src/service/fact/factRetrievers/{entityFactRetriever.ts => catalogFactRetriever.ts} (81%) create mode 100644 plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts diff --git a/packages/backend/src/plugins/techInsights.ts b/packages/backend/src/plugins/techInsights.ts index abab1d133d..6194f03410 100644 --- a/packages/backend/src/plugins/techInsights.ts +++ b/packages/backend/src/plugins/techInsights.ts @@ -17,10 +17,10 @@ import { createRouter, buildTechInsightsContext, createFactRetrieverRegistration, + createCatalogFactRetriever, } from '@backstage/plugin-tech-insights-backend'; import { Router } from 'express'; import { PluginEnvironment } from '../types'; -import { CatalogClient } from '@backstage/catalog-client'; import { JsonRulesEngineFactCheckerFactory, JSON_RULE_ENGINE_CHECK_TYPE, @@ -38,41 +38,10 @@ export default async function createPlugin({ database, discovery, factRetrievers: [ - createFactRetrieverRegistration('5 4 * * 6', { - // Example cron, At 04:05 on Saturday. - id: 'testRetriever', - version: '1.1.2', - entityFilter: [{ kind: 'component' }], // EntityFilter to be used in the future (creating checks, graphs etc.) to figure out which entities this fact retrieves data for. - schema: { - examplenumberfact: { - type: 'integer', - description: 'Example fact returning a number', - }, - }, - handler: async _ctx => { - const catalogClient = new CatalogClient({ - discoveryApi: discovery, - }); - const entities = await catalogClient.getEntities({ - filter: [{ kind: 'component' }], - }); - - return Promise.resolve( - entities.items.map(it => { - return { - entity: { - namespace: it.metadata.namespace!!, - kind: it.kind, - name: it.metadata.name, - }, - facts: { - examplenumberfact: 2, - }, - }; - }), - ); - }, - }), + createFactRetrieverRegistration( + '* * * * *', // Example cron, every minute + createCatalogFactRetriever(), + ), ], factCheckerFactory: new JsonRulesEngineFactCheckerFactory({ checks: [ diff --git a/plugins/tech-insights-backend/src/index.ts b/plugins/tech-insights-backend/src/index.ts index 5e8e690e8d..763c565108 100644 --- a/plugins/tech-insights-backend/src/index.ts +++ b/plugins/tech-insights-backend/src/index.ts @@ -25,3 +25,4 @@ export type { export type { PersistenceContext } from './service/persistence/persistenceContext'; export { createFactRetrieverRegistration } from './service/fact/createFactRetriever'; +export * from './service/fact/factRetrievers'; diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.test.ts similarity index 98% rename from plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts rename to plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.test.ts index a1741c22ee..7b762be63f 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createEntityFactRetriever } from './entityFactRetriever'; +import { createCatalogFactRetriever } from './catalogFactRetriever'; import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; import { PluginEndpointDiscovery, @@ -52,7 +52,7 @@ const defaultEntityListResponse: CatalogListResponse = { spec: { type: 'service', lifecycle: 'test', - owner: 'group:team-a', + owner: 'team-a', }, relations: [ { @@ -106,7 +106,7 @@ const handlerContext = { config: ConfigReader.fromConfigs([]), }; -const entityFactRetriever = createEntityFactRetriever({ +const entityFactRetriever = createCatalogFactRetriever({ annotations: ['backstage.io/techdocs-ref'], }); diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.ts similarity index 81% rename from plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts rename to plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.ts index 1442d4d929..d4d6e54865 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/entityFactRetriever.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/catalogFactRetriever.ts @@ -14,18 +14,23 @@ * limitations under the License. */ -import { FactRetrieverContext } from '@backstage/plugin-tech-insights-node'; +import { + FactRetriever, + FactRetrieverContext, +} from '@backstage/plugin-tech-insights-node'; import { CatalogClient } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import isEmpty from 'lodash/isEmpty'; import camelCase from 'lodash/camelCase'; import { get } from 'lodash'; -export const createEntityFactRetriever = ({ - annotations = [], -}: { +type Options = { annotations?: string[]; -}) => ({ +}; + +export const createCatalogFactRetriever = ( + { annotations = [] }: Options = { annotations: [] }, +): FactRetriever => ({ id: 'entityRetriever', version: '0.0.1', schema: { @@ -39,18 +44,18 @@ export const createEntityFactRetriever = ({ }, hasDescription: { type: 'boolean', - description: 'The entity has an owned_by relation', + description: 'The entity has a description in metadata', }, hasTags: { type: 'boolean', description: 'The entity has tags in metadata', }, - ...annotations.reduce((acc: object, it: string) => { + ...annotations.reduce((acc: object, annotation: string) => { return { ...acc, - [camelCase(`hasAnnotation-${it}`)]: { + [camelCase(`hasAnnotation-${annotation}`)]: { type: 'boolean', - description: `The entity has the annotation: ${it} `, + description: `The entity has the annotation: ${annotation} `, }, }; }, {}), @@ -72,7 +77,7 @@ export const createEntityFactRetriever = ({ hasOwner: Boolean(entity.spec?.owner), hasGroupOwner: Boolean( entity.spec?.owner && - (entity.spec?.owner as string).startsWith('group:'), + !(entity.spec?.owner as string).startsWith('user:'), ), hasDescription: Boolean(entity.metadata?.description), hasTags: !isEmpty(entity.metadata?.tags), diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts new file mode 100644 index 0000000000..58ee8f8e92 --- /dev/null +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts @@ -0,0 +1,16 @@ +/* + * 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 * from './catalogFactRetriever'; From 5c00e450458805513a4bfe035bf53e37d1bf21ea Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 15 Nov 2021 18:48:45 +0000 Subject: [PATCH 042/139] Changeset Signed-off-by: Iain Billett --- .changeset/ten-planes-remember.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/ten-planes-remember.md diff --git a/.changeset/ten-planes-remember.md b/.changeset/ten-planes-remember.md new file mode 100644 index 0000000000..2887aec568 --- /dev/null +++ b/.changeset/ten-planes-remember.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-tech-insights-backend': patch +--- + +Add a catalog fact retriever + +Add a fact retriever which generates facts related to the completeness +of entity data in the catalog. From 8cf89aab389fb5ffd6a5b1adc6dd01580b06e63c Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 15 Nov 2021 19:40:22 +0000 Subject: [PATCH 043/139] Api docs Signed-off-by: Iain Billett --- plugins/tech-insights-backend/api-report.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/tech-insights-backend/api-report.md b/plugins/tech-insights-backend/api-report.md index 826789dd1d..2ed5919484 100644 --- a/plugins/tech-insights-backend/api-report.md +++ b/plugins/tech-insights-backend/api-report.md @@ -24,6 +24,14 @@ export const buildTechInsightsContext: < options: TechInsightsOptions, ) => Promise>; +// Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createCatalogFactRetriever" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const createCatalogFactRetriever: ({ + annotations, +}?: Options) => FactRetriever; + // @public export function createFactRetrieverRegistration( cadence: string, From c736fe24d0fa31d72f82e58bdff49d4ad5305c8a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 17 Nov 2021 11:15:20 +0100 Subject: [PATCH 044/139] Update splunk-on-call.yaml Signed-off-by: Patrik Oldsberg --- microsite/data/plugins/splunk-on-call.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsite/data/plugins/splunk-on-call.yaml b/microsite/data/plugins/splunk-on-call.yaml index 15ceb86a1b..bb0e25111a 100644 --- a/microsite/data/plugins/splunk-on-call.yaml +++ b/microsite/data/plugins/splunk-on-call.yaml @@ -1,7 +1,7 @@ --- title: Splunk On-Call -author: Spotify -authorUrl: https://github.com/spotify +author: ayshiff +authorUrl: https://github.com/ayshiff category: Monitoring description: Splunk On-Call offers a simple way to identify incidents and escalation policies. documentation: https://github.com/backstage/backstage/tree/master/plugins/splunk-on-call From 1c37c3c26cbdeded48b9a033d39ba3ee29266e12 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Nov 2021 18:35:00 +0100 Subject: [PATCH 045/139] core-app-api: deprecate ApiRegistry Signed-off-by: Patrik Oldsberg --- packages/core-app-api/api-report.md | 2 +- packages/core-app-api/src/apis/system/ApiRegistry.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 48b89a6951..cb6ad27cf8 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -126,7 +126,7 @@ export type ApiProviderProps = { children: ReactNode; }; -// @public +// @public @deprecated export class ApiRegistry implements ApiHolder { constructor(apis: Map); // Warning: (ae-forgotten-export) The symbol "ApiRegistryBuilder" needs to be exported by the entry point index.d.ts diff --git a/packages/core-app-api/src/apis/system/ApiRegistry.ts b/packages/core-app-api/src/apis/system/ApiRegistry.ts index 29c8b1cfe4..2d12fcfb35 100644 --- a/packages/core-app-api/src/apis/system/ApiRegistry.ts +++ b/packages/core-app-api/src/apis/system/ApiRegistry.ts @@ -36,6 +36,7 @@ class ApiRegistryBuilder { * A registry for utility APIs. * * @public + * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} instead. */ export class ApiRegistry implements ApiHolder { static builder() { From 57591f57547143d73a70e5a52e94267a9ccc200b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Nov 2021 18:47:50 +0100 Subject: [PATCH 046/139] test-utils: add TestApiProvider Signed-off-by: Patrik Oldsberg --- packages/test-utils/api-report.md | 13 +++ .../src/testUtils/TestApiProvider.tsx | 94 +++++++++++++++++++ packages/test-utils/src/testUtils/index.tsx | 2 + 3 files changed, 109 insertions(+) create mode 100644 packages/test-utils/src/testUtils/TestApiProvider.tsx diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 62a8d9f601..38656137a3 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -5,6 +5,7 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { ApiRef } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ErrorApi } from '@backstage/core-plugin-api'; import { ErrorApiError } from '@backstage/core-plugin-api'; @@ -175,6 +176,18 @@ export function setupRequestMockHandlers(worker: { // @public export type SyncLogCollector = () => void; +// @public +export const TestApiProvider: ({ + apis, + children, +}: TestApiProviderProps) => JSX.Element; + +// @public +export type TestApiProviderProps = { + apis: [...TestApiProviderPropsApiPairs]; + children: ReactNode; +}; + // @public export type TestAppOptions = { routeEntries?: string[]; diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx new file mode 100644 index 0000000000..272da8c807 --- /dev/null +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ReactNode } from 'react'; +import { ApiProvider } from '@backstage/core-app-api'; +import { ApiRef, ApiHolder } from '@backstage/core-plugin-api'; + +type TestApiProviderPropsApiPair = TApi extends infer TImpl + ? [ApiRef, Partial] + : never; + +/** @ignore */ +type TestApiProviderPropsApiPairs = { + [TIndex in keyof TApiPairs]: TestApiProviderPropsApiPair; +}; + +/** + * Properties for the {@link TestApiProvider} component. + * + * @public + */ +export type TestApiProviderProps = { + apis: [...TestApiProviderPropsApiPairs]; + children: ReactNode; +}; + +/** @internal */ +class TestApiRegistry implements ApiHolder { + constructor(private readonly apis: Map) {} + + get(api: ApiRef): T | undefined { + return this.apis.get(api.id) as T | undefined; + } +} + +/** + * An API provider that lets you provide any number of API implementations in + * a test, without necessarily having to implement the full APIs. + * + * A migration from `ApiRegistry` and `ApiProvider` might look like this, from: + * + * ```tsx + * renderInTestApp( + * + * {...} + * + * ) + * ``` + * + * To the following: + * + * ```tsx + * renderInTestApp( + * + * {...} + * + * ) + * ``` + * + * Note that the cast to `IdentityApi` is no longer needed as long as the mock API + * implements a subset of the `IdentityApi`. + * + * @public + **/ +export const TestApiProvider = ({ + apis, + children, +}: TestApiProviderProps) => { + return ( + [api.id, impl]))) + } + children={children} + /> + ); +}; diff --git a/packages/test-utils/src/testUtils/index.tsx b/packages/test-utils/src/testUtils/index.tsx index 7d93d606cc..d48767d3cd 100644 --- a/packages/test-utils/src/testUtils/index.tsx +++ b/packages/test-utils/src/testUtils/index.tsx @@ -22,3 +22,5 @@ export * from './msw'; export * from './Keyboard'; export * from './logCollector'; export * from './testingLibrary'; +export { TestApiProvider } from './TestApiProvider'; +export type { TestApiProviderProps } from './TestApiProvider'; From 5f6d97d044bb0c8e802e4e002b01bd43f32ce6de Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Nov 2021 21:26:29 +0100 Subject: [PATCH 047/139] test-utils: add TestApiRegistry Signed-off-by: Patrik Oldsberg --- .../src/apis/system/ApiRegistry.ts | 2 +- packages/test-utils/api-report.md | 12 +++- .../src/testUtils/TestApiProvider.tsx | 58 ++++++++++++++----- packages/test-utils/src/testUtils/index.tsx | 2 +- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/packages/core-app-api/src/apis/system/ApiRegistry.ts b/packages/core-app-api/src/apis/system/ApiRegistry.ts index 2d12fcfb35..e433381dd1 100644 --- a/packages/core-app-api/src/apis/system/ApiRegistry.ts +++ b/packages/core-app-api/src/apis/system/ApiRegistry.ts @@ -36,7 +36,7 @@ class ApiRegistryBuilder { * A registry for utility APIs. * * @public - * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} instead. + * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} or {@link @backstage/test-utils#TestApiRegistry} instead. */ export class ApiRegistry implements ApiHolder { static builder() { diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 38656137a3..5bb552563b 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -5,6 +5,7 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { ApiHolder } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ErrorApi } from '@backstage/core-plugin-api'; @@ -184,10 +185,19 @@ export const TestApiProvider: ({ // @public export type TestApiProviderProps = { - apis: [...TestApiProviderPropsApiPairs]; + apis: readonly [...TestApiProviderPropsApiPairs]; children: ReactNode; }; +// @public +export class TestApiRegistry implements ApiHolder { + // (undocumented) + get(api: ApiRef): T | undefined; + static with( + ...apis: readonly [...TestApiProviderPropsApiPairs] + ): TestApiRegistry; +} + // @public export type TestAppOptions = { routeEntries?: string[]; diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 272da8c807..92be0ffd77 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -18,8 +18,9 @@ import React, { ReactNode } from 'react'; import { ApiProvider } from '@backstage/core-app-api'; import { ApiRef, ApiHolder } from '@backstage/core-plugin-api'; +/** @ignore */ type TestApiProviderPropsApiPair = TApi extends infer TImpl - ? [ApiRef, Partial] + ? readonly [ApiRef, Partial] : never; /** @ignore */ @@ -33,22 +34,58 @@ type TestApiProviderPropsApiPairs = { * @public */ export type TestApiProviderProps = { - apis: [...TestApiProviderPropsApiPairs]; + apis: readonly [...TestApiProviderPropsApiPairs]; children: ReactNode; }; -/** @internal */ -class TestApiRegistry implements ApiHolder { - constructor(private readonly apis: Map) {} +/** + * The `TestApiRegistry` is an {@link @backstage/core-plugin-api#ApiHolder} implementation + * that is particularly well suited for development and test environments such as + * unit tests, storybooks, and isolated plugin development setups. + * + * @public + */ +export class TestApiRegistry implements ApiHolder { + /** + * Creates a new {@link TestApiRegistry} with a list of API implementation pairs. + * + * Similar to the {@link TestApiProvider}, there is no need to provide a full + * implementation of each API, it's enough to implement the methods that are tested. + * + * @example + * ```ts + * const apis = TestApiRegistry.with( + * [configApiRef, new ConfigReader({})], + * [identityApiRef, { getUserId: () => 'tester' }], + * ); + * ``` + * + * @public + * @param apis - A list of pairs mapping an ApiRef to its respective implementation. + */ + static with( + ...apis: readonly [...TestApiProviderPropsApiPairs] + ) { + return new TestApiRegistry( + new Map(apis.map(([api, impl]) => [api.id, impl])), + ); + } + private constructor(private readonly apis: Map) {} + + /** {@inheritdoc @backstage/core-plugin-api#ApiHolder.get} */ get(api: ApiRef): T | undefined { return this.apis.get(api.id) as T | undefined; } } /** - * An API provider that lets you provide any number of API implementations in - * a test, without necessarily having to implement the full APIs. + * The `TestApiProvider` is a Utility API context provider that is particularly + * well suited for development and test environments such as unit tests, storybooks, + * and isolated plugin development setups. + * + * It lets you provide any number of API implementations, without necessarily + * having to fully implement each of the APIs. * * A migration from `ApiRegistry` and `ApiProvider` might look like this, from: * @@ -84,11 +121,6 @@ export const TestApiProvider = ({ children, }: TestApiProviderProps) => { return ( - [api.id, impl]))) - } - children={children} - /> + ); }; diff --git a/packages/test-utils/src/testUtils/index.tsx b/packages/test-utils/src/testUtils/index.tsx index d48767d3cd..c778c5c837 100644 --- a/packages/test-utils/src/testUtils/index.tsx +++ b/packages/test-utils/src/testUtils/index.tsx @@ -22,5 +22,5 @@ export * from './msw'; export * from './Keyboard'; export * from './logCollector'; export * from './testingLibrary'; -export { TestApiProvider } from './TestApiProvider'; +export { TestApiProvider, TestApiRegistry } from './TestApiProvider'; export type { TestApiProviderProps } from './TestApiProvider'; From 417f80c0c8b3704a3cdebc74a53b42cb41ef889d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 17 Nov 2021 10:44:02 +0100 Subject: [PATCH 048/139] test-utils: add tests for TestApiProvider & Registry Signed-off-by: Patrik Oldsberg --- .../src/testUtils/TestApiProvider.test.tsx | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 packages/test-utils/src/testUtils/TestApiProvider.test.tsx diff --git a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx new file mode 100644 index 0000000000..7bffd39a4a --- /dev/null +++ b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx @@ -0,0 +1,136 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { createApiRef, useApiHolder } from '@backstage/core-plugin-api'; +import { TestApiProvider } from './TestApiProvider'; +import { render, screen } from '@testing-library/react'; +import { TestApiRegistry } from '.'; + +const xApiRef = createApiRef<{ a: string; b: number }>({ + id: 'x', +}); +const yApiRef = createApiRef({ + id: 'y', +}); + +function Verifier() { + const holder = useApiHolder(); + const x = holder.get(xApiRef); + const y = holder.get(yApiRef); + + return ( +
+ {x ? ( + + x={x.a},{x.b} + + ) : ( + no x + )} + {y ? y={y} : no y} +
+ ); +} + +describe('TestApiProvider', () => { + it('should provide APIs', () => { + render( + + + , + ); + expect(screen.getByText('x=a,3')).toBeInTheDocument(); + expect(screen.getByText('y=y')).toBeInTheDocument(); + }); + + it('should provide partial APIs', () => { + render( + + + , + ); + expect(screen.getByText('x=a,')).toBeInTheDocument(); + expect(screen.getByText('no y')).toBeInTheDocument(); + }); + + it('should require partial implementations to still match types', () => { + render( + // @ts-expect-error + + + , + ); + expect(screen.getByText('x=3,')).toBeInTheDocument(); + expect(screen.getByText('no y')).toBeInTheDocument(); + }); + + it('should allow empty APIs', () => { + render( + + + , + ); + expect(screen.getByText('no x')).toBeInTheDocument(); + expect(screen.getByText('no y')).toBeInTheDocument(); + }); +}); + +describe('TestApiRegistry', () => { + it('should be created with APIs', () => { + const x = { a: 'a', b: 3 }; + const y = 'y'; + const registry = TestApiRegistry.with([xApiRef, x], [yApiRef, y]); + + expect(registry.get(xApiRef)).toBe(x); + expect(registry.get(yApiRef)).toBe(y); + }); + + it('should allow partial implementations', () => { + const x = { a: 'a' }; + const registry = TestApiRegistry.with([xApiRef, x]); + + expect(registry.get(xApiRef)).toBe(x); + expect(registry.get(yApiRef)).toBeUndefined(); + }); + + it('should require partial implementations to match types', () => { + const x = { a: 2 }; + // @ts-expect-error + const registry = TestApiRegistry.with([xApiRef, x]); + + expect(registry.get(xApiRef)).toBe(x); + expect(registry.get(yApiRef)).toBeUndefined(); + }); + + it('should prefer last duplicate API that was provided', () => { + const x1 = { a: 'a' }; + const x2 = { a: 's' }; + const x3 = { a: 'd' }; + const registry = TestApiRegistry.with( + [xApiRef, x1], + [xApiRef, x2], + [xApiRef, x3], + ); + + expect(registry.get(xApiRef)).toBe(x3); + }); +}); From b811fadaec2976b977960c483db95a2c1bc94a1b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 17 Nov 2021 10:45:39 +0100 Subject: [PATCH 049/139] core-*: migrate to using TestApiProvider Signed-off-by: Patrik Oldsberg --- .../src/routing/FeatureFlagged.test.tsx | 7 +- .../src/routing/FlatRoutes.test.tsx | 7 +- .../AlertDisplay/AlertDisplay.test.tsx | 84 ++++++++----------- .../CopyTextButton/CopyTextButton.test.tsx | 33 ++++---- .../DismissableBanner.stories.tsx | 29 +++---- .../DismissableBanner.test.tsx | 12 ++- .../src/components/Link/Link.test.tsx | 11 ++- .../ErrorBoundary/ErrorBoundary.test.tsx | 12 +-- .../src/layout/Header/Header.test.tsx | 18 ++-- .../HomepageTimer/HomepageTimer.test.tsx | 12 +-- .../src/extensions/useElementFilter.test.tsx | 11 +-- 11 files changed, 108 insertions(+), 128 deletions(-) diff --git a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx index 2b05c8f61d..d1b45c39d1 100644 --- a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx +++ b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx @@ -16,14 +16,15 @@ import React from 'react'; import { FeatureFlagged } from './FeatureFlagged'; import { render } from '@testing-library/react'; -import { ApiProvider, ApiRegistry, LocalStorageFeatureFlags } from '../apis'; +import { LocalStorageFeatureFlags } from '../apis'; +import { TestApiProvider } from '@backstage/test-utils'; import { featureFlagsApiRef } from '@backstage/core-plugin-api'; const mockFeatureFlagsApi = new LocalStorageFeatureFlags(); const Wrapper = ({ children }: { children?: React.ReactNode }) => ( - + {children} - + ); describe('FeatureFlagged', () => { diff --git a/packages/core-app-api/src/routing/FlatRoutes.test.tsx b/packages/core-app-api/src/routing/FlatRoutes.test.tsx index 0df296cc59..ca247d46cd 100644 --- a/packages/core-app-api/src/routing/FlatRoutes.test.tsx +++ b/packages/core-app-api/src/routing/FlatRoutes.test.tsx @@ -17,17 +17,18 @@ import { render, RenderResult } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter, Route, Routes, useOutlet } from 'react-router-dom'; -import { ApiProvider, ApiRegistry, LocalStorageFeatureFlags } from '../apis'; +import { LocalStorageFeatureFlags } from '../apis'; import { featureFlagsApiRef } from '@backstage/core-plugin-api'; import { AppContext } from '../app'; import { AppContextProvider } from '../app/AppContext'; import { FlatRoutes } from './FlatRoutes'; +import { TestApiProvider } from '@backstage/test-utils'; const mockFeatureFlagsApi = new LocalStorageFeatureFlags(); const Wrapper = ({ children }: { children?: React.ReactNode }) => ( - + {children} - + ); function makeRouteRenderer(node: ReactNode) { diff --git a/packages/core-components/src/components/AlertDisplay/AlertDisplay.test.tsx b/packages/core-components/src/components/AlertDisplay/AlertDisplay.test.tsx index d6bf990f8a..477057f31b 100644 --- a/packages/core-components/src/components/AlertDisplay/AlertDisplay.test.tsx +++ b/packages/core-components/src/components/AlertDisplay/AlertDisplay.test.tsx @@ -17,78 +17,66 @@ import React from 'react'; import { AlertDisplay } from './AlertDisplay'; import { alertApiRef } from '@backstage/core-plugin-api'; -import { - ApiProvider, - ApiRegistry, - AlertApiForwarder, -} from '@backstage/core-app-api'; +import { AlertApiForwarder } from '@backstage/core-app-api'; import Observable from 'zen-observable'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; const TEST_MESSAGE = 'TEST_MESSAGE'; describe('', () => { it('renders without exploding', async () => { - const apiRegistry = ApiRegistry.from([ - [alertApiRef, new AlertApiForwarder()], - ]); - const { queryByText } = await renderInTestApp( - + - , + , ); expect(queryByText(TEST_MESSAGE)).not.toBeInTheDocument(); }); it('renders with message', async () => { - const apiRegistry = ApiRegistry.from([ - [ - alertApiRef, - { - post() {}, - alert$() { - return Observable.of({ message: TEST_MESSAGE }); - }, - }, - ], - ]); - const { queryByText } = await renderInTestApp( - + - , + , ); expect(queryByText(TEST_MESSAGE)).toBeInTheDocument(); }); describe('with multiple messages', () => { - let apiRegistry: ApiRegistry; - - beforeEach(() => { - apiRegistry = ApiRegistry.from([ - [ - alertApiRef, - { - post() {}, - alert$() { - return Observable.of( - { message: 'message one' }, - { message: 'message two' }, - { message: 'message three' }, - ); - }, + const apis = [ + [ + alertApiRef, + { + post() {}, + alert$() { + return Observable.of( + { message: 'message one' }, + { message: 'message two' }, + { message: 'message three' }, + ); }, - ], - ]); - }); + }, + ] as const, + ] as const; it('renders first message', async () => { const { queryByText } = await renderInTestApp( - + - , + , ); expect(queryByText('message one')).toBeInTheDocument(); @@ -96,9 +84,9 @@ describe('', () => { it('renders a count of remaining messages', async () => { const { queryByText } = await renderInTestApp( - + - , + , ); expect(queryByText('(2 older messages)')).toBeInTheDocument(); diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx index b84d3919e8..a9532dd79b 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx @@ -17,10 +17,9 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { CopyTextButton } from './CopyTextButton'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; -import { errorApiRef, ErrorApi } from '@backstage/core-plugin-api'; +import { errorApiRef } from '@backstage/core-plugin-api'; import { useCopyToClipboard } from 'react-use'; jest.mock('popper.js', () => { @@ -51,22 +50,18 @@ const props = { tooltipText: 'mockTooltip', }; -const apiRegistry = ApiRegistry.from([ - [ - errorApiRef, - { - post: jest.fn(), - error$: jest.fn(), - } as ErrorApi, - ], -]); +const mockErrorApi = { + post: jest.fn(), + error$: jest.fn(), +}; +const apis = [[errorApiRef, mockErrorApi] as const] as const; describe('', () => { it('renders without exploding', async () => { const { getByTitle, queryByText } = await renderInTestApp( - + - , + , ); expect(getByTitle('mockTooltip')).toBeInTheDocument(); expect(queryByText('mockTooltip')).not.toBeInTheDocument(); @@ -80,9 +75,9 @@ describe('', () => { spy.mockReturnValue([{}, copy]); const rendered = await renderInTestApp( - + - , + , ); const button = rendered.getByTitle('mockTooltip'); fireEvent.click(button); @@ -101,10 +96,10 @@ describe('', () => { spy.mockReturnValue([{ error }, jest.fn()]); await renderInTestApp( - + - , + , ); - expect(apiRegistry.get(errorApiRef)?.post).toHaveBeenCalledWith(error); + expect(mockErrorApi.post).toHaveBeenCalledWith(error); }); }); diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx index e0e9a89c9f..5487d2e7b5 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx @@ -18,12 +18,13 @@ import React from 'react'; import { DismissableBanner } from './DismissableBanner'; import Link from '@material-ui/core/Link'; import Typography from '@material-ui/core/Typography'; -import { ApiProvider, ApiRegistry, WebStorage } from '@backstage/core-app-api'; +import { WebStorage } from '@backstage/core-app-api'; import { ErrorApi, storageApiRef, StorageApi, } from '@backstage/core-plugin-api'; +import { TestApiProvider } from '@backstage/test-utils'; export default { title: 'Feedback/DismissableBanner', @@ -37,47 +38,47 @@ const createWebStorage = (): StorageApi => { return WebStorage.create({ errorApi }); }; -const apis = ApiRegistry.from([[storageApiRef, createWebStorage()]]); +const apis = [[storageApiRef, createWebStorage()] as const]; export const Default = () => (
- + - +
); export const Error = () => (
- + - +
); export const EmojisIncluded = () => (
- + - +
); export const WithLink = () => (
- + @@ -90,29 +91,29 @@ export const WithLink = () => ( variant="info" id="linked_dismissable" /> - +
); export const Fixed = () => (
- + - +
); export const Warning = () => (
- + - +
); diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx index ddc119369b..50b1fd33eb 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx @@ -16,13 +16,17 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; -import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import { + renderWithEffects, + TestApiRegistry, + wrapInTestApp, +} from '@backstage/test-utils'; import { DismissableBanner } from './DismissableBanner'; -import { ApiRegistry, ApiProvider, WebStorage } from '@backstage/core-app-api'; +import { ApiProvider, WebStorage } from '@backstage/core-app-api'; import { storageApiRef, StorageApi } from '@backstage/core-plugin-api'; describe('', () => { - let apis: ApiRegistry; + let apis: TestApiRegistry; const mockErrorApi = { post: jest.fn(), error$: jest.fn() }; const createWebStorage = (): StorageApi => { return WebStorage.create({ @@ -31,7 +35,7 @@ describe('', () => { }; beforeEach(() => { - apis = ApiRegistry.from([[storageApiRef, createWebStorage()]]); + apis = TestApiRegistry.with([storageApiRef, createWebStorage()]); }); it('renders the message and the popover', async () => { diff --git a/packages/core-components/src/components/Link/Link.test.tsx b/packages/core-components/src/components/Link/Link.test.tsx index df1bcfac03..2d00aad827 100644 --- a/packages/core-components/src/components/Link/Link.test.tsx +++ b/packages/core-components/src/components/Link/Link.test.tsx @@ -16,8 +16,11 @@ import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; -import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { + MockAnalyticsApi, + TestApiProvider, + wrapInTestApp, +} from '@backstage/test-utils'; import { analyticsApiRef } from '@backstage/core-plugin-api'; import { isExternalUri, Link } from './Link'; import { Route, Routes } from 'react-router'; @@ -48,11 +51,11 @@ describe('', () => { const { getByText } = render( wrapInTestApp( - + {linkText} - , + , ), ); diff --git a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx index a1c45bff4d..361ad123bf 100644 --- a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx +++ b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx @@ -20,9 +20,9 @@ import { ErrorBoundary } from './ErrorBoundary'; import { MockErrorApi, renderInTestApp, + TestApiProvider, withLogCollector, } from '@backstage/test-utils'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; import { errorApiRef } from '@backstage/core-plugin-api'; type BombProps = { @@ -41,25 +41,25 @@ const Bomb = ({ shouldThrow }: BombProps) => { describe('', () => { it('should render error boundary with and without error', async () => { const { error } = await withLogCollector(['error'], async () => { - const apis = ApiRegistry.with(errorApiRef, new MockErrorApi()); + const errorApi = new MockErrorApi(); const { rerender, queryByRole, getByRole, getByText } = await renderInTestApp( - + - , + , ); expect(queryByRole('alert')).not.toBeInTheDocument(); expect(getByText(/working component/i)).toBeInTheDocument(); rerender( - + - , + , ); expect(getByRole('alert')).toBeInTheDocument(); diff --git a/packages/core-components/src/layout/Header/Header.test.tsx b/packages/core-components/src/layout/Header/Header.test.tsx index 69d46cd102..776e39045c 100644 --- a/packages/core-components/src/layout/Header/Header.test.tsx +++ b/packages/core-components/src/layout/Header/Header.test.tsx @@ -15,13 +15,9 @@ */ import React from 'react'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { Header } from './Header'; -import { - ApiRegistry, - ConfigReader, - ApiProvider, -} from '@backstage/core-app-api'; +import { ConfigReader } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; jest.mock('react-helmet', () => { @@ -72,14 +68,12 @@ describe('
', () => { }); it('should use app.title', async () => { - const apiRegistry = ApiRegistry.with( - configApiRef, - new ConfigReader({ app: { title: 'Blah' } }), - ); const rendered = await renderInTestApp( - +
, - , + , ); rendered.getAllByText(/Title | Blah/); }); diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx index 1d4bf6954a..3d9dfe70a1 100644 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx +++ b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx @@ -14,16 +14,12 @@ * limitations under the License. */ -import { renderWithEffects } from '@backstage/test-utils'; +import { renderWithEffects, TestApiProvider } from '@backstage/test-utils'; import { HomepageTimer } from './HomepageTimer'; import React from 'react'; import { lightTheme } from '@backstage/theme'; import { ThemeProvider } from '@material-ui/core/styles'; -import { - ApiProvider, - ApiRegistry, - ConfigReader, -} from '@backstage/core-app-api'; +import { ConfigReader } from '@backstage/core-app-api'; import { ConfigApi, configApiRef } from '@backstage/core-plugin-api'; it('changes default timezone to GMT', async () => { @@ -41,9 +37,9 @@ it('changes default timezone to GMT', async () => { const rendered = await renderWithEffects( - + - + , ); diff --git a/packages/core-plugin-api/src/extensions/useElementFilter.test.tsx b/packages/core-plugin-api/src/extensions/useElementFilter.test.tsx index 4d42a0c969..8795ecdded 100644 --- a/packages/core-plugin-api/src/extensions/useElementFilter.test.tsx +++ b/packages/core-plugin-api/src/extensions/useElementFilter.test.tsx @@ -18,11 +18,8 @@ import { useElementFilter } from './useElementFilter'; import { renderHook } from '@testing-library/react-hooks'; import { attachComponentData } from './componentData'; import { featureFlagsApiRef } from '../apis'; -import { - ApiProvider, - ApiRegistry, - LocalStorageFeatureFlags, -} from '@backstage/core-app-api'; +import { LocalStorageFeatureFlags } from '@backstage/core-app-api'; +import { TestApiProvider } from '@backstage/test-utils'; const WRAPPING_COMPONENT_KEY = 'core.blob.testing'; const INNER_COMPONENT_KEY = 'core.blob2.testing'; @@ -45,9 +42,9 @@ const FeatureFlagComponent = (_props: { attachComponentData(FeatureFlagComponent, 'core.featureFlagged', true); const mockFeatureFlagsApi = new LocalStorageFeatureFlags(); const Wrapper = ({ children }: { children?: React.ReactNode }) => ( - + {children} - + ); describe('useElementFilter', () => { From 000190de69035e5cd92eb7f20c70c2401bb52f54 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 17 Nov 2021 11:48:42 +0100 Subject: [PATCH 050/139] changesets: add changeset for ApiRegistry deprecation Signed-off-by: Patrik Oldsberg --- .changeset/wet-seas-deliver.md | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .changeset/wet-seas-deliver.md diff --git a/.changeset/wet-seas-deliver.md b/.changeset/wet-seas-deliver.md new file mode 100644 index 0000000000..0b440cbb46 --- /dev/null +++ b/.changeset/wet-seas-deliver.md @@ -0,0 +1,63 @@ +--- +'@backstage/core-app-api': patch +'@backstage/test-utils': patch +--- + +The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`. + +These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs. + +When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code: + +```tsx +render( + + {...} + +) +``` + +Would be migrated to this: + +```tsx +render( + + {...} + +) +``` + +In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.with()`, and it is slightly different from the existing `.with()` method on `ApiRegistry`. + +Usage that looks like this: + +```ts +const apis = ApiRegistry.with( + identityApiRef, + mockIdentityApi as unknown as IdentityApi, +).with(configApiRef, new ConfigReader({})); +``` + +OR like this: + +```ts +const apis = ApiRegistry.from([ + [identityApiRef, mockIdentityApi as unknown as IdentityApi], + [configApiRef, new ConfigReader({})], +]); +``` + +Would be migrated to this: + +```ts +const apis = TestApiRegistry.with( + [identityApiRef, mockIdentityApi], + [configApiRef, new ConfigReader({})], +); +``` + +If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`. From bafa085a2aa9ad4905c27a47451f74a89f47e1f3 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 17 Nov 2021 07:37:36 -0600 Subject: [PATCH 051/139] Improvements from review feedback Signed-off-by: Andre Wanlin --- .../azure-devops/src/api/AzureDevOpsClient.ts | 27 ++++++++++++---- .../PullRequestStatusButtonGroup.tsx | 32 ++++++------------- .../PullRequestTable/PullRequestTable.tsx | 4 +-- .../azure-devops/src/hooks/usePullRequests.ts | 6 ++-- plugins/azure-devops/src/routes.ts | 4 +-- 5 files changed, 38 insertions(+), 35 deletions(-) diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index ec727f4973..cbcc496725 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -42,9 +42,15 @@ export class AzureDevOpsClient implements AzureDevOpsApi { repoName: string, options?: RepoBuildOptions, ): Promise<{ items: RepoBuild[] }> { - const items = await this.get( - `repo-builds/${projectName}/${repoName}?top=${options?.top}`, - ); + const queryString = new URLSearchParams(); + if (options?.top) { + queryString.append('top', options.top.toString()); + } + const urlSegment = `repo-builds/${encodeURIComponent( + projectName, + )}/${encodeURIComponent(repoName)}?${queryString}`; + + const items = await this.get(urlSegment); return { items }; } @@ -53,9 +59,18 @@ export class AzureDevOpsClient implements AzureDevOpsApi { repoName: string, options?: PullRequestOptions, ): Promise<{ items: PullRequest[] }> { - const items = await this.get( - `pull-requests/${projectName}/${repoName}?top=${options?.top}&status=${options?.status}`, - ); + const queryString = new URLSearchParams(); + if (options?.top) { + queryString.append('top', options.top.toString()); + } + if (options?.status) { + queryString.append('status', options.status.toString()); + } + const urlSegment = `pull-requests/${encodeURIComponent( + projectName, + )}/${encodeURIComponent(repoName)}?${queryString}`; + + const items = await this.get(urlSegment); return { items }; } diff --git a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx index 23a8738adc..f516bfb0e5 100644 --- a/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx +++ b/plugins/azure-devops/src/components/PullRequestStatusButtonGroup/PullRequestStatusButtonGroup.tsx @@ -20,46 +20,34 @@ import { PullRequestStatus } from '@backstage/plugin-azure-devops-common'; import React from 'react'; export const PullRequestStatusButtonGroup = ({ - pullRequestStatusState, - setPullRequestsStatusState, + status, + setStatus, }: { - pullRequestStatusState: PullRequestStatus; - setPullRequestsStatusState: (pullRequestStatus: PullRequestStatus) => void; + status: PullRequestStatus; + setStatus: (pullRequestStatus: PullRequestStatus) => void; }) => { return (
{TABS.map(tab => ( - + {tab.component} ))} From bd98d4f457b8132f9fbe348d9cb0a3ee3a0ba126 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 27 Oct 2021 11:52:28 +0200 Subject: [PATCH 073/139] chore: more deprecations and reworking more routeRefs to subRouteRefs Signed-off-by: blam --- plugins/catalog/src/components/Router.tsx | 6 ++-- .../BuildsPage/lib/CITable/CITable.tsx | 28 +++++++++++-------- plugins/circleci/src/components/Router.tsx | 6 ++-- plugins/circleci/src/route-refs.tsx | 6 ++-- plugins/cloudbuild/src/components/Router.tsx | 10 ++----- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 25 ++++++++++------- plugins/cloudbuild/src/routes.ts | 6 ++-- .../github-actions/src/components/Router.tsx | 9 ++---- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 21 +++++++------- plugins/github-actions/src/routes.ts | 7 +++-- 10 files changed, 66 insertions(+), 58 deletions(-) diff --git a/plugins/catalog/src/components/Router.tsx b/plugins/catalog/src/components/Router.tsx index 9116c0b551..2a0eacf074 100644 --- a/plugins/catalog/src/components/Router.tsx +++ b/plugins/catalog/src/components/Router.tsx @@ -16,8 +16,6 @@ import { ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import { AsyncEntityProvider, - entityRoute, - rootRoute, useEntity, useEntityFromUrl, } from '@backstage/plugin-catalog-react'; @@ -87,9 +85,9 @@ export const Router = ({ EntityPage?: ComponentType; }) => ( - } /> + } /> diff --git a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx index b33e1b40cc..510fd20dac 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -26,7 +26,7 @@ import { import RetryIcon from '@material-ui/icons/Replay'; import GitHubIcon from '@material-ui/icons/GitHub'; import LaunchIcon from '@material-ui/icons/Launch'; -import { Link as RouterLink, generatePath } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router-dom'; import { durationHumanized, relativeTimeTo } from '../../../../util'; import { circleCIBuildRouteRef } from '../../../../route-refs'; import { @@ -38,6 +38,7 @@ import { Table, TableColumn, } from '@backstage/core-components'; +import { useRouteRef } from '@backstage/core-plugin-api'; export type CITableBuildInfo = { id: string; @@ -144,16 +145,21 @@ const generatedColumns: TableColumn[] = [ field: 'buildName', highlight: true, width: '20%', - render: (row: Partial) => ( - - {row.buildName ? row.buildName : row?.workflow?.name} - - ), + render: (row: Partial) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const routeLink = useRouteRef(circleCIBuildRouteRef); + + return ( + + {row.buildName ? row.buildName : row?.workflow?.name} + + ); + }, }, { title: 'Job', diff --git a/plugins/circleci/src/components/Router.tsx b/plugins/circleci/src/components/Router.tsx index 1bc13d6327..5aa8dd9df9 100644 --- a/plugins/circleci/src/components/Router.tsx +++ b/plugins/circleci/src/components/Router.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { Routes, Route } from 'react-router'; -import { circleCIRouteRef, circleCIBuildRouteRef } from '../route-refs'; +import { circleCIBuildRouteRef } from '../route-refs'; import { BuildWithStepsPage } from './BuildWithStepsPage/'; import { BuildsPage } from './BuildsPage'; import { CIRCLECI_ANNOTATION } from '../constants'; @@ -41,9 +41,9 @@ export const Router = (_props: Props) => { return ( - } /> + } /> } /> diff --git a/plugins/circleci/src/route-refs.tsx b/plugins/circleci/src/route-refs.tsx index 14e43e5c8d..e8579b9f0d 100644 --- a/plugins/circleci/src/route-refs.tsx +++ b/plugins/circleci/src/route-refs.tsx @@ -14,12 +14,14 @@ * limitations under the License. */ -import { createRouteRef } from '@backstage/core-plugin-api'; +import { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; export const circleCIRouteRef = createRouteRef({ id: 'circle-ci', }); -export const circleCIBuildRouteRef = createRouteRef({ +export const circleCIBuildRouteRef = createSubRouteRef({ id: 'circle-ci/build', + parent: circleCIRouteRef, + path: '/:buildId', }); diff --git a/plugins/cloudbuild/src/components/Router.tsx b/plugins/cloudbuild/src/components/Router.tsx index f9b9a8974f..bdeae7e8fd 100644 --- a/plugins/cloudbuild/src/components/Router.tsx +++ b/plugins/cloudbuild/src/components/Router.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; import { Routes, Route } from 'react-router'; -import { rootRouteRef, buildRouteRef } from '../routes'; +import { buildRouteRef } from '../routes'; import { WorkflowRunDetails } from './WorkflowRunDetails'; import { WorkflowRunsTable } from './WorkflowRunsTable'; import { CLOUDBUILD_ANNOTATION } from './useProjectName'; @@ -40,15 +40,11 @@ export const Router = (_props: Props) => { } return ( + } /> } - /> - } /> - ) ); }; diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 0d00347183..aefa140349 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { Link, Typography, Box, IconButton, Tooltip } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import GoogleIcon from '@material-ui/icons/CloudCircle'; -import { Link as RouterLink, generatePath } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router-dom'; import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import SyncIcon from '@material-ui/icons/Sync'; @@ -26,6 +26,7 @@ import { Entity } from '@backstage/catalog-model'; import { buildRouteRef } from '../../routes'; import { DateTime } from 'luxon'; import { Table, TableColumn } from '@backstage/core-components'; +import { useRouteRef } from '@backstage/core-plugin-api'; const generatedColumns: TableColumn[] = [ { @@ -54,15 +55,19 @@ const generatedColumns: TableColumn[] = [ field: 'source', highlight: true, width: '200px', - render: (row: Partial) => ( - - {row.message} - - ), + render: (row: Partial) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const routeLink = useRouteRef(buildRouteRef); + return ( + + {row.message} + + ); + }, }, { title: 'Ref', diff --git a/plugins/cloudbuild/src/routes.ts b/plugins/cloudbuild/src/routes.ts index 78050fd88e..3737e6948f 100644 --- a/plugins/cloudbuild/src/routes.ts +++ b/plugins/cloudbuild/src/routes.ts @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createRouteRef } from '@backstage/core-plugin-api'; +import { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ id: 'cloudbuild', }); -export const buildRouteRef = createRouteRef({ +export const buildRouteRef = createSubRouteRef({ id: 'cloudbuild/run', + path: '/:id', + parent: rootRouteRef, }); diff --git a/plugins/github-actions/src/components/Router.tsx b/plugins/github-actions/src/components/Router.tsx index cbf29d1d9d..ea07213c45 100644 --- a/plugins/github-actions/src/components/Router.tsx +++ b/plugins/github-actions/src/components/Router.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; import { Routes, Route } from 'react-router'; -import { rootRouteRef, buildRouteRef } from '../routes'; +import { buildRouteRef } from '../routes'; import { WorkflowRunDetails } from './WorkflowRunDetails'; import { WorkflowRunsTable } from './WorkflowRunsTable'; import { GITHUB_ACTIONS_ANNOTATION } from './useProjectName'; @@ -41,12 +41,9 @@ export const Router = (_props: Props) => { } return ( + } /> } - /> - } /> ) diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 51f26bf5d7..d80ecc5d52 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -24,7 +24,7 @@ import { } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import GitHubIcon from '@material-ui/icons/GitHub'; -import { Link as RouterLink, generatePath } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router-dom'; import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import SyncIcon from '@material-ui/icons/Sync'; @@ -34,7 +34,7 @@ import { Entity } from '@backstage/catalog-model'; import { readGitHubIntegrationConfigs } from '@backstage/integration'; import { EmptyState, Table, TableColumn } from '@backstage/core-components'; -import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; const generatedColumns: TableColumn[] = [ { @@ -47,14 +47,15 @@ const generatedColumns: TableColumn[] = [ title: 'Message', field: 'message', highlight: true, - render: (row: Partial) => ( - - {row.message} - - ), + render: (row: Partial) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const routeLink = useRouteRef(buildRouteRef); + return ( + + {row.message} + + ); + }, }, { title: 'Source', diff --git a/plugins/github-actions/src/routes.ts b/plugins/github-actions/src/routes.ts index 4da027ba4c..8e854fad45 100644 --- a/plugins/github-actions/src/routes.ts +++ b/plugins/github-actions/src/routes.ts @@ -14,13 +14,14 @@ * limitations under the License. */ -import { createRouteRef } from '@backstage/core-plugin-api'; +import { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ id: 'github-actions', }); -export const buildRouteRef = createRouteRef({ +export const buildRouteRef = createSubRouteRef({ id: 'github-actions/build', - params: ['id'], + path: '/:id', + parent: rootRouteRef, }); From c67e023eae1dc6e37c9a551c1fd040258f81a2e6 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 29 Oct 2021 11:12:57 +0200 Subject: [PATCH 074/139] chore: putting the deprecations back and fixing last occurence Signed-off-by: blam --- packages/core-plugin-api/src/routing/RouteRef.ts | 8 ++++---- packages/core-plugin-api/src/routing/types.ts | 2 +- plugins/badges/src/api/BadgesClient.ts | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/core-plugin-api/src/routing/RouteRef.ts b/packages/core-plugin-api/src/routing/RouteRef.ts index d62504615a..499866a435 100644 --- a/packages/core-plugin-api/src/routing/RouteRef.ts +++ b/packages/core-plugin-api/src/routing/RouteRef.ts @@ -78,10 +78,10 @@ export class RouteRefImpl } } - // /** @deprecated use `useRouteRef` instead */ - // get path() { - // return thuis - // } + /** @deprecated use `useRouteRef` instead */ + get path() { + return this; + } get icon() { return this.config.icon; diff --git a/packages/core-plugin-api/src/routing/types.ts b/packages/core-plugin-api/src/routing/types.ts index 750dab879f..bd419abb76 100644 --- a/packages/core-plugin-api/src/routing/types.ts +++ b/packages/core-plugin-api/src/routing/types.ts @@ -88,7 +88,7 @@ export type RouteRef = { // TODO(Rugvip): Remove all of these once plugins don't rely on the path /** @deprecated paths are no longer accessed directly from RouteRefs, use useRouteRef instead */ - // path: string; + path: string; /** @deprecated icons are no longer accessed via RouteRefs */ icon?: OldIconComponent; /** @deprecated titles are no longer accessed via RouteRefs */ diff --git a/plugins/badges/src/api/BadgesClient.ts b/plugins/badges/src/api/BadgesClient.ts index d81b042ce5..432a485a6b 100644 --- a/plugins/badges/src/api/BadgesClient.ts +++ b/plugins/badges/src/api/BadgesClient.ts @@ -17,7 +17,6 @@ import { generatePath } from 'react-router'; import { ResponseError } from '@backstage/errors'; import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model'; -import { entityRoute } from '@backstage/plugin-catalog-react'; import { BadgesApi, BadgeSpec } from './types'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; @@ -53,7 +52,7 @@ export class BadgesClient implements BadgesApi { private async getEntityBadgeSpecsUrl(entity: Entity): Promise { const routeParams = this.getEntityRouteParams(entity); - const path = generatePath(entityRoute.path, routeParams); + const path = generatePath(`:kind/:namespace/:name`, routeParams); return `${await this.discoveryApi.getBaseUrl( 'badges', )}/entity/${path}/badge-specs`; From f4477bc907b4bc1f00551e35dbd9ee28f717bd58 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 8 Nov 2021 17:44:28 +0100 Subject: [PATCH 075/139] chore: fix the return type I missed Signed-off-by: blam --- packages/core-plugin-api/src/routing/RouteRef.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-plugin-api/src/routing/RouteRef.ts b/packages/core-plugin-api/src/routing/RouteRef.ts index 499866a435..26e63c8f3a 100644 --- a/packages/core-plugin-api/src/routing/RouteRef.ts +++ b/packages/core-plugin-api/src/routing/RouteRef.ts @@ -80,7 +80,7 @@ export class RouteRefImpl /** @deprecated use `useRouteRef` instead */ get path() { - return this; + return this.config.path ?? ''; } get icon() { From 54310852ad22ccaa4da99fa385340160618c87b7 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 9 Nov 2021 09:16:47 +0100 Subject: [PATCH 076/139] chore: updating api RouteRef Signed-off-by: blam --- plugins/circleci/api-report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/circleci/api-report.md b/plugins/circleci/api-report.md index 25d4af0923..bb4334c0f9 100644 --- a/plugins/circleci/api-report.md +++ b/plugins/circleci/api-report.md @@ -16,7 +16,9 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { GitType } from 'circleci-api'; import { Me } from 'circleci-api'; +import { PathParams } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +import { SubRouteRef } from '@backstage/core-plugin-api'; export { BuildStepAction }; @@ -68,7 +70,7 @@ export const circleCIApiRef: ApiRef; // Warning: (ae-missing-release-tag) "circleCIBuildRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const circleCIBuildRouteRef: RouteRef; +export const circleCIBuildRouteRef: SubRouteRef>; // Warning: (ae-missing-release-tag) "circleCIPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // From a125278b81f9b3f6066951626505e827b9dc3922 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 9 Nov 2021 09:18:04 +0100 Subject: [PATCH 077/139] chore: added changeset Signed-off-by: blam --- .changeset/fluffy-moles-deny.md | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .changeset/fluffy-moles-deny.md diff --git a/.changeset/fluffy-moles-deny.md b/.changeset/fluffy-moles-deny.md new file mode 100644 index 0000000000..1f353806f4 --- /dev/null +++ b/.changeset/fluffy-moles-deny.md @@ -0,0 +1,43 @@ +--- +'@backstage/core-plugin-api': patch +'@backstage/plugin-allure': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-azure-devops': patch +'@backstage/plugin-badges': patch +'@backstage/plugin-bazaar': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-circleci': patch +'@backstage/plugin-cloudbuild': patch +'@backstage/plugin-code-coverage': patch +'@backstage/plugin-config-schema': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-firehydrant': patch +'@backstage/plugin-gcp-projects': patch +'@backstage/plugin-git-release-manager': patch +'@backstage/plugin-github-actions': patch +'@backstage/plugin-gitops-profiles': patch +'@backstage/plugin-home': patch +'@backstage/plugin-ilert': patch +'@backstage/plugin-jenkins': patch +'@backstage/plugin-kafka': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-newrelic': patch +'@backstage/plugin-pagerduty': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-search': patch +'@backstage/plugin-sentry': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-todo': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-xcmetrics': patch +--- + +Refactor out the deprecated path and icon from RouteRefs From b94af61a2497118cccf60249bf7e113bf7652518 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 9 Nov 2021 09:49:22 +0100 Subject: [PATCH 078/139] chore: remove core-plugin-api from this changeset as it's covered in another Signed-off-by: blam --- .changeset/fluffy-moles-deny.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/fluffy-moles-deny.md b/.changeset/fluffy-moles-deny.md index 1f353806f4..4b4abc36fa 100644 --- a/.changeset/fluffy-moles-deny.md +++ b/.changeset/fluffy-moles-deny.md @@ -1,5 +1,4 @@ --- -'@backstage/core-plugin-api': patch '@backstage/plugin-allure': patch '@backstage/plugin-api-docs': patch '@backstage/plugin-azure-devops': patch From 2e3e2c2cbeacef987ec759cc395ebc0b4ef4242e Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 12 Nov 2021 00:29:18 +0100 Subject: [PATCH 079/139] chore: fixing tests for cloudbuild plugins Signed-off-by: blam --- .../components/WorkflowRunsTable/WorkflowRunsTable.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx index ac28c5bedf..0152f9d964 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx @@ -18,6 +18,7 @@ import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; import { WorkflowRunsTableView } from './WorkflowRunsTable'; import { WorkflowRun } from '../useWorkflowRuns'; +import { rootRouteRef } from '../../routes'; describe('', () => { let runs: WorkflowRun[] = []; @@ -56,6 +57,7 @@ describe('', () => { runs={runs} total={runs.length} />, + { mountedRoutes: { '/': rootRouteRef } }, ); expect(getByTestId('cell-source')).toHaveAttribute('href', '/run_id_1'); @@ -74,6 +76,7 @@ describe('', () => { runs={runs} total={runs.length} />, + { mountedRoutes: { '/': rootRouteRef } }, ); expect(getByTestId('cell-created')).toHaveTextContent( @@ -94,6 +97,7 @@ describe('', () => { runs={runs} total={runs.length} />, + { mountedRoutes: { '/': rootRouteRef } }, ); const rerunActionElement = getByTestId('action-rerun'); From 7c55fae20872d74e65fcaf3ad8909f6c53665105 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 20:34:46 +0100 Subject: [PATCH 080/139] chore: code review comments and renaming some ids for routeRefs Signed-off-by: blam --- plugins/catalog-react/src/routes.ts | 2 +- plugins/cost-insights/src/plugin.ts | 4 ++-- plugins/gcp-projects/src/routes.ts | 4 ++-- plugins/gitops-profiles/src/routes.ts | 2 +- plugins/jenkins/src/plugin.ts | 2 +- plugins/lighthouse/src/plugin.ts | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/catalog-react/src/routes.ts b/plugins/catalog-react/src/routes.ts index bb19b6c312..73c44d1a4a 100644 --- a/plugins/catalog-react/src/routes.ts +++ b/plugins/catalog-react/src/routes.ts @@ -25,7 +25,7 @@ export const rootRoute = createRouteRef({ export const catalogRouteRef = rootRoute; export const entityRoute = createRouteRef({ - id: 'catalog/entity', + id: 'catalog-entity', params: ['namespace', 'kind', 'name'], }); diff --git a/plugins/cost-insights/src/plugin.ts b/plugins/cost-insights/src/plugin.ts index 884e458418..4c37cd7531 100644 --- a/plugins/cost-insights/src/plugin.ts +++ b/plugins/cost-insights/src/plugin.ts @@ -25,11 +25,11 @@ export const rootRouteRef = createRouteRef({ }); export const projectGrowthAlertRef = createRouteRef({ - id: 'cost-insights/investigating-growth', + id: 'cost-insights-investigating-growth', }); export const unlabeledDataflowAlertRef = createRouteRef({ - id: 'cost-insights/labeling-jobs', + id: 'cost-insights-labeling-jobs', }); export const costInsightsPlugin = createPlugin({ diff --git a/plugins/gcp-projects/src/routes.ts b/plugins/gcp-projects/src/routes.ts index de4c39fb7d..2429f12cae 100644 --- a/plugins/gcp-projects/src/routes.ts +++ b/plugins/gcp-projects/src/routes.ts @@ -20,8 +20,8 @@ export const rootRouteRef = createRouteRef({ id: 'gcp-projects', }); export const projectRouteRef = createRouteRef({ - id: 'gcp-projects/project', + id: 'gcp-projects-project', }); export const newProjectRouteRef = createRouteRef({ - id: 'gcp-projects/new', + id: 'gcp-projects-new', }); diff --git a/plugins/gitops-profiles/src/routes.ts b/plugins/gitops-profiles/src/routes.ts index 0a100c8215..8ee9612be0 100644 --- a/plugins/gitops-profiles/src/routes.ts +++ b/plugins/gitops-profiles/src/routes.ts @@ -21,7 +21,7 @@ export const gitOpsClusterListRoute = createRouteRef({ }); export const gitOpsClusterDetailsRoute = createRouteRef({ - id: 'gitops-cluster/:owner/:repo', + id: 'gitops-cluster-details', params: ['owner', 'repo'], }); diff --git a/plugins/jenkins/src/plugin.ts b/plugins/jenkins/src/plugin.ts index e38cf55f54..c32c9b50d1 100644 --- a/plugins/jenkins/src/plugin.ts +++ b/plugins/jenkins/src/plugin.ts @@ -31,7 +31,7 @@ export const rootRouteRef = createRouteRef({ }); export const buildRouteRef = createSubRouteRef({ - id: 'jenkins/build', + id: 'jenkins/builds', path: '/builds/:jobFullName/:buildNumber', parent: rootRouteRef, }); diff --git a/plugins/lighthouse/src/plugin.ts b/plugins/lighthouse/src/plugin.ts index e5274204e2..4ce7cc62b5 100644 --- a/plugins/lighthouse/src/plugin.ts +++ b/plugins/lighthouse/src/plugin.ts @@ -29,15 +29,15 @@ export const rootRouteRef = createRouteRef({ }); export const viewAuditRouteRef = createRouteRef({ - id: 'audit/:id', + id: 'lighthouse-audit', }); export const createAuditRouteRef = createRouteRef({ - id: 'create-audit', + id: 'lighthouse-create-audit', }); export const entityContentRouteRef = createRouteRef({ - id: 'entity-content', + id: 'lighthouse-entity-content', }); export const lighthousePlugin = createPlugin({ From 06065c6003d64bd02a6203e9907cfd36de9e1b44 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 20:43:14 +0100 Subject: [PATCH 081/139] chore: fixed better namespacing for the routeRefs Signed-off-by: blam --- plugins/allure/src/plugin.ts | 2 +- plugins/catalog-react/src/routes.ts | 2 +- plugins/cost-insights/src/plugin.ts | 4 ++-- plugins/gcp-projects/src/routes.ts | 4 ++-- plugins/gitops-profiles/src/routes.ts | 4 ++-- plugins/lighthouse/src/plugin.ts | 6 +++--- plugins/search/src/plugin.ts | 2 +- plugins/techdocs/src/routes.ts | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/allure/src/plugin.ts b/plugins/allure/src/plugin.ts index d1df347467..a2eaa06a38 100644 --- a/plugins/allure/src/plugin.ts +++ b/plugins/allure/src/plugin.ts @@ -23,7 +23,7 @@ import { import { AllureApiClient, allureApiRef } from './api'; export const allureRouteRef = createRouteRef({ - id: 'allure-report', + id: 'allure', }); export const allurePlugin = createPlugin({ diff --git a/plugins/catalog-react/src/routes.ts b/plugins/catalog-react/src/routes.ts index 73c44d1a4a..f29d52f08a 100644 --- a/plugins/catalog-react/src/routes.ts +++ b/plugins/catalog-react/src/routes.ts @@ -25,7 +25,7 @@ export const rootRoute = createRouteRef({ export const catalogRouteRef = rootRoute; export const entityRoute = createRouteRef({ - id: 'catalog-entity', + id: 'catalog:entity', params: ['namespace', 'kind', 'name'], }); diff --git a/plugins/cost-insights/src/plugin.ts b/plugins/cost-insights/src/plugin.ts index 4c37cd7531..1714c5fbd5 100644 --- a/plugins/cost-insights/src/plugin.ts +++ b/plugins/cost-insights/src/plugin.ts @@ -25,11 +25,11 @@ export const rootRouteRef = createRouteRef({ }); export const projectGrowthAlertRef = createRouteRef({ - id: 'cost-insights-investigating-growth', + id: 'cost-insights:investigating-growth', }); export const unlabeledDataflowAlertRef = createRouteRef({ - id: 'cost-insights-labeling-jobs', + id: 'cost-insights:labeling-jobs', }); export const costInsightsPlugin = createPlugin({ diff --git a/plugins/gcp-projects/src/routes.ts b/plugins/gcp-projects/src/routes.ts index 2429f12cae..f527393f5a 100644 --- a/plugins/gcp-projects/src/routes.ts +++ b/plugins/gcp-projects/src/routes.ts @@ -20,8 +20,8 @@ export const rootRouteRef = createRouteRef({ id: 'gcp-projects', }); export const projectRouteRef = createRouteRef({ - id: 'gcp-projects-project', + id: 'gcp-projects:project', }); export const newProjectRouteRef = createRouteRef({ - id: 'gcp-projects-new', + id: 'gcp-projects:new', }); diff --git a/plugins/gitops-profiles/src/routes.ts b/plugins/gitops-profiles/src/routes.ts index 8ee9612be0..b79e5686d2 100644 --- a/plugins/gitops-profiles/src/routes.ts +++ b/plugins/gitops-profiles/src/routes.ts @@ -21,10 +21,10 @@ export const gitOpsClusterListRoute = createRouteRef({ }); export const gitOpsClusterDetailsRoute = createRouteRef({ - id: 'gitops-cluster-details', + id: 'gitops-cluster:details', params: ['owner', 'repo'], }); export const gitOpsClusterCreateRoute = createRouteRef({ - id: 'gitops-cluster-create', + id: 'gitops-cluster:create', }); diff --git a/plugins/lighthouse/src/plugin.ts b/plugins/lighthouse/src/plugin.ts index 4ce7cc62b5..1c9bc295f8 100644 --- a/plugins/lighthouse/src/plugin.ts +++ b/plugins/lighthouse/src/plugin.ts @@ -29,15 +29,15 @@ export const rootRouteRef = createRouteRef({ }); export const viewAuditRouteRef = createRouteRef({ - id: 'lighthouse-audit', + id: 'lighthouse:audit', }); export const createAuditRouteRef = createRouteRef({ - id: 'lighthouse-create-audit', + id: 'lighthouse:create-audit', }); export const entityContentRouteRef = createRouteRef({ - id: 'lighthouse-entity-content', + id: 'lighthouse:entity-content', }); export const lighthousePlugin = createPlugin({ diff --git a/plugins/search/src/plugin.ts b/plugins/search/src/plugin.ts index fbefc03b6c..9dda2a84bb 100644 --- a/plugins/search/src/plugin.ts +++ b/plugins/search/src/plugin.ts @@ -30,7 +30,7 @@ export const rootRouteRef = createRouteRef({ }); export const rootNextRouteRef = createRouteRef({ - id: 'search-next', + id: 'search:next', }); export const searchPlugin = createPlugin({ diff --git a/plugins/techdocs/src/routes.ts b/plugins/techdocs/src/routes.ts index 8df5792e00..e781550899 100644 --- a/plugins/techdocs/src/routes.ts +++ b/plugins/techdocs/src/routes.ts @@ -17,14 +17,14 @@ import { createRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ - id: 'techdocs-index-page', + id: 'techdocs:index-page', }); export const rootDocsRouteRef = createRouteRef({ - id: 'techdocs-reader-page', + id: 'techdocs:reader-page', params: ['namespace', 'kind', 'name'], }); export const rootCatalogDocsRouteRef = createRouteRef({ - id: 'catalog-techdocs-reader-view', + id: 'techdocs:catalog-reader-view', }); From 4d6d2012d1d99f59fd6dc8e7fe8731d470811fe3 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 21:01:03 +0100 Subject: [PATCH 082/139] chore: this package wasn't affected with the change from the PR Signed-off-by: blam --- .changeset/yellow-falcons-whisper.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/yellow-falcons-whisper.md b/.changeset/yellow-falcons-whisper.md index 9168b32c27..63a5d42fb0 100644 --- a/.changeset/yellow-falcons-whisper.md +++ b/.changeset/yellow-falcons-whisper.md @@ -1,6 +1,5 @@ --- '@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-scaffolder-common': patch --- Expose template metadata to custom action handler in Scaffolder. From 3f7931bedce241f9d304cf0a55083fb3e772a106 Mon Sep 17 00:00:00 2001 From: therynamo Date: Wed, 17 Nov 2021 14:05:57 -0600 Subject: [PATCH 083/139] feat: Allow SSE For S3 Buckets Signed-off-by: therynamo --- packages/techdocs-cli/src/commands/index.ts | 1 + .../techdocs-cli/src/lib/PublisherConfig.test.ts | 14 ++++++++++++++ packages/techdocs-cli/src/lib/PublisherConfig.ts | 1 + 3 files changed, 16 insertions(+) diff --git a/packages/techdocs-cli/src/commands/index.ts b/packages/techdocs-cli/src/commands/index.ts index 280072cfd9..d4f41cf7a3 100644 --- a/packages/techdocs-cli/src/commands/index.ts +++ b/packages/techdocs-cli/src/commands/index.ts @@ -161,6 +161,7 @@ export function registerCommands(program: CommanderStatic) { '--awsEndpoint ', 'Optional AWS endpoint to send requests to.', ) + .option('--awsS3sse ', 'Optional AWS S3 Server Side Encryption.') .option( '--awsS3ForcePathStyle', 'Optional AWS S3 option to force path style.', diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.test.ts b/packages/techdocs-cli/src/lib/PublisherConfig.test.ts index 519972c03d..88f542c049 100644 --- a/packages/techdocs-cli/src/lib/PublisherConfig.test.ts +++ b/packages/techdocs-cli/src/lib/PublisherConfig.test.ts @@ -80,6 +80,20 @@ describe('getValidPublisherConfig', () => { actualConfig.getString('techdocs.publisher.awsS3.bucketRootPath'), ).toBe('backstage-data/techdocs'); }); + + it('should return valid ConfigReader with SSE option', () => { + const config = { + publisherType: 'awsS3', + storageName: 'someStorageName', + awsS3sse: 'aws:kms', + } as unknown as Command; + + const actualConfig = PublisherConfig.getValidConfig(config); + expect(actualConfig.getString('techdocs.publisher.type')).toBe('awsS3'); + expect(actualConfig.getString('techdocs.publisher.awsS3.sse')).toBe( + 'aws:kms', + ); + }); }); describe('for openStackSwift', () => { diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.ts b/packages/techdocs-cli/src/lib/PublisherConfig.ts index e855d573ce..d4779995c0 100644 --- a/packages/techdocs-cli/src/lib/PublisherConfig.ts +++ b/packages/techdocs-cli/src/lib/PublisherConfig.ts @@ -89,6 +89,7 @@ export class PublisherConfig { ...(cmd.awsRoleArn && { credentials: { roleArn: cmd.awsRoleArn } }), ...(cmd.awsEndpoint && { endpoint: cmd.awsEndpoint }), ...(cmd.awsS3ForcePathStyle && { s3ForcePathStyle: true }), + ...(cmd.awsS3sse && { sse: cmd.awsS3sse }), }, }; } From f2694e3750e6ce73f32d639eafc9720e160894b4 Mon Sep 17 00:00:00 2001 From: therynamo Date: Wed, 17 Nov 2021 14:08:14 -0600 Subject: [PATCH 084/139] changeset Signed-off-by: therynamo --- .changeset/techdocs-real-geese-dress.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/techdocs-real-geese-dress.md diff --git a/.changeset/techdocs-real-geese-dress.md b/.changeset/techdocs-real-geese-dress.md new file mode 100644 index 0000000000..3c440935d7 --- /dev/null +++ b/.changeset/techdocs-real-geese-dress.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +Adds ability to use encrypted s3 buckets by utilizing the SSE option in the aws sdk From e9ad91717e21dde1614fac74c161ede54601a9ce Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 21:28:40 +0100 Subject: [PATCH 085/139] chore: bumping dockerode and testcontainers dependency to fix ssh2 Signed-off-by: blam --- yarn.lock | 138 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 60 deletions(-) diff --git a/yarn.lock b/yarn.lock index debcf79d8e..3188052baf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6763,6 +6763,13 @@ dependencies: "@types/glob" "*" +"@types/archiver@^5.1.1": + version "5.1.1" + resolved "https://registry.npmjs.org/@types/archiver/-/archiver-5.1.1.tgz#d6d7610de4386b293abd5c1cb1875e0a4f4e1c30" + integrity sha512-heuaCk0YH5m274NOLSi66H1zX6GtZoMsdE6TYFcpFFjBjg0FoU4i4/M/a/kNlgNg26Xk3g364mNOYe1JaiEPOQ== + dependencies: + "@types/glob" "*" + "@types/argparse@1.0.38": version "1.0.38" resolved "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" @@ -7060,14 +7067,14 @@ integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA== "@types/docker-modem@*": - version "3.0.1" - resolved "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.1.tgz#2a29c914dd64c4c9213510d2ce75fa85232934df" - integrity sha512-ZUXPF0WNnvs7AxoQRijt+DW2jsXnWCk8ac28tTYzTpBNnOEIAw83A+pYkCXjTFdJHMTc+wUmwNr71Zy2TRjlWg== + version "3.0.2" + resolved "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz#c49c902e17364fc724e050db5c1d2b298c6379d4" + integrity sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ== dependencies: "@types/node" "*" "@types/ssh2" "*" -"@types/dockerode@^3.2.1": +"@types/dockerode@^3.2.1", "@types/dockerode@^3.2.5": version "3.3.0" resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz#d6afcc1de31e211ee900da3a48ef4f1a496cf05a" integrity sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg== @@ -8010,10 +8017,10 @@ "@types/node" "*" "@types/ssh2-streams" "*" -"@types/ssh2@^0.5.45": - version "0.5.46" - resolved "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.46.tgz#e12341a242aea0e98ac2dec89e039bf421fd3584" - integrity sha512-1pC8FHrMPYdkLoUOwTYYifnSEPzAFZRsp3JFC/vokQ+dRrVI+hDBwz0SNmQ3pL6h39OSZlPs0uCG7wKJkftnaA== +"@types/ssh2@^0.5.48": + version "0.5.48" + resolved "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.48.tgz#0d9e8654a76eaaf4cfeaeb88d74c4489cfcf7aea" + integrity sha512-cmQu0gp/6RtDXe1r2xXGgi0V0TeCdueDSRMEvBX8cTRT/sSREkUpgCYZLyh+iI8Ql+VNV8Az9toQoYa/IdgHbQ== dependencies: "@types/node" "*" "@types/ssh2-streams" "*" @@ -9028,7 +9035,7 @@ any-observable@^0.3.0: resolved "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== -any-promise@^1.0.0, any-promise@^1.1.0: +any-promise@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= @@ -9467,7 +9474,7 @@ asn1.js@^4.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -asn1@^0.2.4, asn1@~0.2.0, asn1@~0.2.3: +asn1@^0.2.4, asn1@~0.2.3: version "0.2.4" resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== @@ -11884,6 +11891,13 @@ cp-file@^7.0.0: nested-error-stacks "^2.0.0" p-event "^4.1.0" +cpu-features@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz#9f636156f1155fd04bdbaa028bb3c2fbef3cea7a" + integrity sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA== + dependencies: + nan "^2.14.1" + cpy@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/cpy/-/cpy-8.1.1.tgz#066ed4c6eaeed9577df96dae4db9438c1a90df62" @@ -13078,27 +13092,27 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" -docker-compose@^0.23.10: - version "0.23.12" - resolved "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.12.tgz#fa883b98be08f6926143d06bf9e522ef7ed3210c" - integrity sha512-KFbSMqQBuHjTGZGmYDOCO0L4SaML3BsWTId5oSUyaBa22vALuFHNv+UdDWs3HcMylHWKsxCbLB7hnM/nCosWZw== +docker-compose@^0.23.13: + version "0.23.13" + resolved "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.13.tgz#77d37bd05b6a966345f631e6d05e961c79514f06" + integrity sha512-/9fYC4g3AO+qsqxIZhmbVnFvJJPcYEV2yJbAPPXH+6AytU3urIY8lUAXOlvY8sl4u25pdKu1JrOfAmWC7lJDJg== dependencies: yaml "^1.10.2" docker-modem@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.0.tgz#cb912ad8daed42f858269fb3be6944df281ec12d" - integrity sha512-WwFajJ8I5geZ/dDZ5FDMDA6TBkWa76xWwGIGw8uzUjNUGCN0to83wJ8Oi1AxrJTC0JBn+7fvIxUctnawtlwXeg== + version "3.0.3" + resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz#ac4bb1f32f81ac2e7120c5e99a068fab2458a32f" + integrity sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw== dependencies: debug "^4.1.1" readable-stream "^3.5.0" split-ca "^1.0.1" - ssh2 "^0.8.7" + ssh2 "^1.4.0" -dockerode@^3.2.1: - version "3.3.0" - resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.0.tgz#bedaf48ef9fa9124275a54a9881a92374c51008e" - integrity sha512-St08lfOjpYCOXEM8XA0VLu3B3hRjtddODphNW5GFoA0AS3JHgoPQKOz0Qmdzg3P+hUPxhb02g1o1Cu1G+U3lRg== +dockerode@^3.2.1, dockerode@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz#74f66e239e092e7910e2beae6322d35c44b08cdc" + integrity sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ== dependencies: docker-modem "^3.0.0" tar-fs "~2.0.1" @@ -15430,6 +15444,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" @@ -20928,6 +20954,11 @@ nan@^2.12.1, nan@^2.14.0: resolved "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== +nan@^2.14.1, nan@^2.15.0: + version "2.15.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + nano-css@^5.1.0, nano-css@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.3.1.tgz#b709383e07ad3be61f64edffacb9d98250b87a1f" @@ -26208,29 +26239,24 @@ sqlstring@^2.3.2: resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== -ssh-remote-port-forward@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.3.tgz#074cebed6d54a42ce4659d6aa24987e433ff5fef" - integrity sha512-PJ6qGFmB6n0iMCQIp+qzx8qVUE5cgacopvEh63t3NJjEQHOaza/JT9zywmmVlaol/eGtCmpvBXx2A03ih1Y+xg== +ssh-remote-port-forward@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" + integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== dependencies: - "@types/ssh2" "^0.5.45" - ssh2 "^0.8.9" + "@types/ssh2" "^0.5.48" + ssh2 "^1.4.0" -ssh2-streams@~0.4.10: - version "0.4.10" - resolved "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" - integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ== +ssh2@^1.4.0: + version "1.5.0" + resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.5.0.tgz#4dc559ba98a1cbb420e8d42998dfe35d0eda92bc" + integrity sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA== dependencies: - asn1 "~0.2.0" + asn1 "^0.2.4" bcrypt-pbkdf "^1.0.2" - streamsearch "~0.1.2" - -ssh2@^0.8.7, ssh2@^0.8.9: - version "0.8.9" - resolved "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3" - integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw== - dependencies: - ssh2-streams "~0.4.10" + optionalDependencies: + cpu-features "0.0.2" + nan "^2.15.0" sshpk@^1.7.0: version "1.16.1" @@ -26424,13 +26450,6 @@ stream-shift@^1.0.0: resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-array@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" - integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M= - dependencies: - any-promise "^1.1.0" - stream-transform@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.2.tgz#3cb7a14c802eb39bc40caaab0535e584f3a65caf" @@ -26438,7 +26457,7 @@ stream-transform@^2.0.1: dependencies: mixme "^0.3.1" -streamsearch@0.1.2, streamsearch@~0.1.2: +streamsearch@0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= @@ -27238,22 +27257,21 @@ test-exclude@^6.0.0: minimatch "^3.0.4" testcontainers@^7.10.0: - version "7.11.1" - resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-7.11.1.tgz#b3810badf79433ba02f210683eec1a1c488c107d" - integrity sha512-lfZeys5bLkADjOaoXfQy0V0+G8sGKr8ESANz7MhSVBwC+OTTxkP3+FVwP48bW4mwRcQ4Hojwbfw10OYT80QZmQ== + version "7.23.0" + resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-7.23.0.tgz#7d73a5e219a970fb75ff6a23a28dace8b7f3f232" + integrity sha512-90H1iijeIjOLp7WVNYKTNkM1sd+dlW5019ns45hSPcOET43WebEZQVJl8/Ag9vwSZD2mjomMum9a/EXk/st4sQ== dependencies: - "@types/archiver" "^5.1.0" - "@types/dockerode" "^3.2.1" + "@types/archiver" "^5.1.1" + "@types/dockerode" "^3.2.5" archiver "^5.3.0" byline "^5.0.0" - debug "^4.3.1" - docker-compose "^0.23.10" - dockerode "^3.2.1" + debug "^4.3.2" + docker-compose "^0.23.13" + dockerode "^3.3.1" get-port "^5.1.1" - glob "^7.1.7" + glob "^7.2.0" slash "^3.0.0" - ssh-remote-port-forward "^1.0.3" - stream-to-array "^2.3.0" + ssh-remote-port-forward "^1.0.4" tar-fs "^2.1.1" text-encoding-utf-8@^1.0.1: From fb4377ac53c8abc829f86cb680c877efd27564a9 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 21:32:43 +0100 Subject: [PATCH 086/139] chore: updating the bump in package.jsons too as it a security risk for the ssh2 packages Signed-off-by: blam --- packages/backend-common/package.json | 4 ++-- packages/backend-test-utils/package.json | 2 +- packages/backend/package.json | 4 ++-- .../templates/default-app/packages/backend/package.json.hbs | 4 ++-- packages/techdocs-cli/package.json | 4 ++-- plugins/techdocs-backend/package.json | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 16ce62c7d7..c46545c4d3 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -39,7 +39,7 @@ "@lerna/project": "^4.0.0", "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", - "@types/dockerode": "^3.2.1", + "@types/dockerode": "^3.3.1", "@types/express": "^4.17.6", "archiver": "^5.0.2", "aws-sdk": "^2.840.0", @@ -47,7 +47,7 @@ "concat-stream": "^2.0.0", "cors": "^2.8.5", "cross-fetch": "^3.0.6", - "dockerode": "^3.2.1", + "dockerode": "^3.3.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 2f29c2412b..299b5588a2 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -37,7 +37,7 @@ "mysql2": "^2.2.5", "pg": "^8.3.0", "sqlite3": "^5.0.1", - "testcontainers": "^7.10.0", + "testcontainers": "^7.23.0", "uuid": "^8.0.0" }, "devDependencies": { diff --git a/packages/backend/package.json b/packages/backend/package.json index 4c854241d4..a05bdae692 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -55,7 +55,7 @@ "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^11.0.1", - "dockerode": "^3.2.1", + "dockerode": "^3.3.1", "example-app": "^0.2.51", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -69,7 +69,7 @@ }, "devDependencies": { "@backstage/cli": "^0.8.2", - "@types/dockerode": "^3.2.1", + "@types/dockerode": "^3.3.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" }, diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 194892d40f..8fc193c911 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -29,7 +29,7 @@ "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", - "dockerode": "^3.2.1", + "dockerode": "^3.3.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", "knex": "^0.21.6", @@ -43,7 +43,7 @@ }, "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", - "@types/dockerode": "^3.2.1", + "@types/dockerode": "^3.3.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" }, diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index a24a1be0f8..ecdf488571 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -60,9 +60,9 @@ "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", "@backstage/techdocs-common": "^0.10.4", - "@types/dockerode": "^3.2.1", + "@types/dockerode": "^3.3.1", "commander": "^6.1.0", - "dockerode": "^3.2.1", + "dockerode": "^3.3.1", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", "react-dev-utils": "^11.0.4", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index c71833948b..1524a8300e 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -41,7 +41,7 @@ "@backstage/techdocs-common": "^0.10.5", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", - "dockerode": "^3.2.1", + "dockerode": "^3.3.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", @@ -53,7 +53,7 @@ "devDependencies": { "@backstage/cli": "^0.8.1", "@backstage/test-utils": "^0.1.20", - "@types/dockerode": "^3.2.1", + "@types/dockerode": "^3.3.1", "msw": "^0.35.0", "supertest": "^6.1.3" }, From e21e3c6102c620205b37e11dff0b217caa101fc5 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Nov 2021 21:33:55 +0100 Subject: [PATCH 087/139] chore: add changeset Signed-off-by: blam --- .changeset/honest-shrimps-dance.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/honest-shrimps-dance.md diff --git a/.changeset/honest-shrimps-dance.md b/.changeset/honest-shrimps-dance.md new file mode 100644 index 0000000000..8c7852b96b --- /dev/null +++ b/.changeset/honest-shrimps-dance.md @@ -0,0 +1,9 @@ +--- +'@backstage/backend-common': patch +'@backstage/backend-test-utils': patch +'@backstage/create-app': patch +'@techdocs/cli': patch +'@backstage/plugin-techdocs-backend': patch +--- + +Bumping minimum requirements for `dockerode` and `testcontainers` From d266c30243f4b239775fd2322b89526d9ee9aa0a Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Thu, 18 Nov 2021 14:12:31 +0800 Subject: [PATCH 088/139] add missing installation steps for the todo plugin Signed-off-by: Gauthier Roebroeck --- plugins/todo-backend/README.md | 13 +++++++ plugins/todo/README.md | 71 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/plugins/todo-backend/README.md b/plugins/todo-backend/README.md index 61ca5202d0..fd3410e42e 100644 --- a/plugins/todo-backend/README.md +++ b/plugins/todo-backend/README.md @@ -36,6 +36,19 @@ export default async function createPlugin({ } ``` +And then add to `packages/backend/src/index.ts`: + +```js +// In packages/backend/src/index.ts +import todo from './plugins/todo'; +// ... +async function main() { + // ... + const todoEnv = useHotMemoize(module, () => createEnv('todo')); + // ... + apiRouter.use('/todo', await kafka(todoEnv)); +``` + ## Scanned Files The included `TodoReaderService` and `TodoScmReader` works by reading source code of to the entity that is being viewed. The location source code is determined by the value of the [`backstage.io/source-location` diff --git a/plugins/todo/README.md b/plugins/todo/README.md index 4165582b65..3e1c370d8a 100644 --- a/plugins/todo/README.md +++ b/plugins/todo/README.md @@ -2,6 +2,77 @@ This plugin lists `// TODO` comments in source code. It currently exports a single component extension for use on entity pages. +## Setup + +1. Run: + +```bash +# From your Backstage root directory +yarn --cwd packages/app add @backstage/plugin-todo +yarn --cwd packages/backend add @backstage/plugin-todo-backend +``` + +2. Add the plugin backend: + +In a new file named `todo.ts` under `backend/src/plugins`: + +```js +import { Router } from 'express'; +import { CatalogClient } from '@backstage/catalog-client'; +import { + createRouter, + TodoReaderService, + TodoScmReader, +} from '@backstage/plugin-todo-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + reader, + config, + discovery, +}: PluginEnvironment): Promise { + const todoReader = TodoScmReader.fromConfig(config, { + logger, + reader, + }); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + const todoService = new TodoReaderService({ + todoReader, + catalogClient, + }); + + return await createRouter({ todoService }); +} +``` + +And then add to `packages/backend/src/index.ts`: + +```js +// In packages/backend/src/index.ts +import todo from './plugins/todo'; +// ... +async function main() { + // ... + const todoEnv = useHotMemoize(module, () => createEnv('todo')); + // ... + apiRouter.use('/todo', await kafka(todoEnv)); +``` + +3. Add the plugin as a tab to your service entities: + +```jsx +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityTodoContent } from '@backstage/plugin-todo'; + +const serviceEntityPage = ( + + {/* other tabs... */} + + + +``` + ## Format The default parser uses [Leasot](https://github.com/pgilad/leasot), which supports a wide range of languages. By default it supports the `TODO` and `FIXME` tags, along with `@` prefix and author reference through with either a `()` suffix or trailing `/`. For more information on how to configure the parser, see `@backstage/plugin-todo-backend`. From 8cc6a92593382ce2a667c6b5bbcb4800471d6bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Nov 2021 09:45:28 +0100 Subject: [PATCH 089/139] appease vale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/techdocs-real-geese-dress.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/techdocs-real-geese-dress.md b/.changeset/techdocs-real-geese-dress.md index 3c440935d7..0d5f1978f9 100644 --- a/.changeset/techdocs-real-geese-dress.md +++ b/.changeset/techdocs-real-geese-dress.md @@ -2,4 +2,4 @@ '@techdocs/cli': patch --- -Adds ability to use encrypted s3 buckets by utilizing the SSE option in the aws sdk +Adds ability to use encrypted S3 buckets by utilizing the SSE option in the AWS SDK From f029733d1312418631c9e8b672d6b62efa822256 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Nov 2021 10:17:15 +0100 Subject: [PATCH 090/139] chore: remove occurrences of the rule of hooks to favour wrapping the components instead Signed-off-by: blam --- .../BuildsPage/lib/CITable/CITable.tsx | 27 +++++----- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 25 +++++----- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 17 ++++--- .../BuildsPage/lib/CITable/CITable.tsx | 49 ++++++++++--------- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx index 510fd20dac..12919bd937 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -146,19 +146,22 @@ const generatedColumns: TableColumn[] = [ highlight: true, width: '20%', render: (row: Partial) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const routeLink = useRouteRef(circleCIBuildRouteRef); + const LinkWrapper: React.FC<{}> = () => { + const routeLink = useRouteRef(circleCIBuildRouteRef); - return ( - - {row.buildName ? row.buildName : row?.workflow?.name} - - ); + return ( + + {row.buildName ? row.buildName : row?.workflow?.name} + + ); + }; + + return ; }, }, { diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index aefa140349..4c24e9f790 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -56,17 +56,20 @@ const generatedColumns: TableColumn[] = [ highlight: true, width: '200px', render: (row: Partial) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const routeLink = useRouteRef(buildRouteRef); - return ( - - {row.message} - - ); + const LinkWrapper: React.FC<{}> = () => { + const routeLink = useRouteRef(buildRouteRef); + return ( + + {row.message} + + ); + }; + + return ; }, }, { diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index d80ecc5d52..d31b5d372a 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -48,13 +48,16 @@ const generatedColumns: TableColumn[] = [ field: 'message', highlight: true, render: (row: Partial) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const routeLink = useRouteRef(buildRouteRef); - return ( - - {row.message} - - ); + const LinkWrapper: React.FC<{}> = () => { + const routeLink = useRouteRef(buildRouteRef); + return ( + + {row.message} + + ); + }; + + return ; }, }, { diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index 7a7b77f5dc..426142fd20 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -92,30 +92,33 @@ const generatedColumns: TableColumn[] = [ field: 'fullName', highlight: true, render: (row: Partial) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const routeLink = useRouteRef(buildRouteRef); - if (!row.fullName || !row.lastBuild?.number) { - return ( - <> - {row.fullName || - row.fullDisplayName || - row.displayName || - 'Unknown'} - - ); - } + const LinkWrapper: React.FC<{}> = () => { + const routeLink = useRouteRef(buildRouteRef); + if (!row.fullName || !row.lastBuild?.number) { + return ( + <> + {row.fullName || + row.fullDisplayName || + row.displayName || + 'Unknown'} + + ); + } - return ( - - {row.fullDisplayName} - - ); + return ( + + {row.fullDisplayName} + + ); + }; + + return ; }, }, { From d73ae571634056bb21728ec6bd833b89b508b781 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Nov 2021 10:24:04 +0100 Subject: [PATCH 091/139] docs: updating the docs saying that it is now possible to provide your own Field Extensions Signed-off-by: blam --- docs/features/software-templates/writing-templates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index f95177e029..81f2622312 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -284,8 +284,8 @@ to publish to. And it can be any host that is listed in your `integrations` config in `app-config.yaml`. The `RepoUrlPicker` is a custom field that we provide part of the -`plugin-scaffolder`. It's currently not possible to create your own fields yet, -but contributions are welcome! :) +`plugin-scaffolder`. You can provide your own custom fields by +[writing your own Custom Field Extensions](./writing-custom-field-extensions.md) #### The Owner Picker From 610e3c979dee3ed9017019c5d1b4772f804a7579 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Nov 2021 10:35:49 +0100 Subject: [PATCH 092/139] chore: remove occurrences of FC Signed-off-by: blam --- .../circleci/src/components/BuildsPage/lib/CITable/CITable.tsx | 2 +- .../src/components/WorkflowRunsTable/WorkflowRunsTable.tsx | 2 +- .../src/components/WorkflowRunsTable/WorkflowRunsTable.tsx | 2 +- .../jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx index 12919bd937..53cbc754cf 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -146,7 +146,7 @@ const generatedColumns: TableColumn[] = [ highlight: true, width: '20%', render: (row: Partial) => { - const LinkWrapper: React.FC<{}> = () => { + const LinkWrapper = () => { const routeLink = useRouteRef(circleCIBuildRouteRef); return ( diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 4c24e9f790..d79d481cd4 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -56,7 +56,7 @@ const generatedColumns: TableColumn[] = [ highlight: true, width: '200px', render: (row: Partial) => { - const LinkWrapper: React.FC<{}> = () => { + const LinkWrapper = () => { const routeLink = useRouteRef(buildRouteRef); return ( ) => { - const LinkWrapper: React.FC<{}> = () => { + const LinkWrapper = () => { const routeLink = useRouteRef(buildRouteRef); return ( diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index 426142fd20..39187fe859 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -92,7 +92,7 @@ const generatedColumns: TableColumn[] = [ field: 'fullName', highlight: true, render: (row: Partial) => { - const LinkWrapper: React.FC<{}> = () => { + const LinkWrapper = () => { const routeLink = useRouteRef(buildRouteRef); if (!row.fullName || !row.lastBuild?.number) { return ( From 0c92f73a111b3c3d40ddbc3d1a0e28fca0324f4a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 18 Nov 2021 10:37:12 +0100 Subject: [PATCH 093/139] test-utils: TestApiRegistry.with -> .from Signed-off-by: Patrik Oldsberg --- .changeset/wet-seas-deliver.md | 2 +- .../DismissableBanner/DismissableBanner.test.tsx | 2 +- packages/test-utils/api-report.md | 2 +- .../test-utils/src/testUtils/TestApiProvider.test.tsx | 8 ++++---- packages/test-utils/src/testUtils/TestApiProvider.tsx | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.changeset/wet-seas-deliver.md b/.changeset/wet-seas-deliver.md index 0b440cbb46..f625e093f5 100644 --- a/.changeset/wet-seas-deliver.md +++ b/.changeset/wet-seas-deliver.md @@ -54,7 +54,7 @@ const apis = ApiRegistry.from([ Would be migrated to this: ```ts -const apis = TestApiRegistry.with( +const apis = TestApiRegistry.from( [identityApiRef, mockIdentityApi], [configApiRef, new ConfigReader({})], ); diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx index 50b1fd33eb..e62b3fa82d 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.test.tsx @@ -35,7 +35,7 @@ describe('', () => { }; beforeEach(() => { - apis = TestApiRegistry.with([storageApiRef, createWebStorage()]); + apis = TestApiRegistry.from([storageApiRef, createWebStorage()]); }); it('renders the message and the popover', async () => { diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 5bb552563b..a1f22a7a64 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -193,7 +193,7 @@ export type TestApiProviderProps = { export class TestApiRegistry implements ApiHolder { // (undocumented) get(api: ApiRef): T | undefined; - static with( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ): TestApiRegistry; } diff --git a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx index 7bffd39a4a..d3f84f854a 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx @@ -98,7 +98,7 @@ describe('TestApiRegistry', () => { it('should be created with APIs', () => { const x = { a: 'a', b: 3 }; const y = 'y'; - const registry = TestApiRegistry.with([xApiRef, x], [yApiRef, y]); + const registry = TestApiRegistry.from([xApiRef, x], [yApiRef, y]); expect(registry.get(xApiRef)).toBe(x); expect(registry.get(yApiRef)).toBe(y); @@ -106,7 +106,7 @@ describe('TestApiRegistry', () => { it('should allow partial implementations', () => { const x = { a: 'a' }; - const registry = TestApiRegistry.with([xApiRef, x]); + const registry = TestApiRegistry.from([xApiRef, x]); expect(registry.get(xApiRef)).toBe(x); expect(registry.get(yApiRef)).toBeUndefined(); @@ -115,7 +115,7 @@ describe('TestApiRegistry', () => { it('should require partial implementations to match types', () => { const x = { a: 2 }; // @ts-expect-error - const registry = TestApiRegistry.with([xApiRef, x]); + const registry = TestApiRegistry.from([xApiRef, x]); expect(registry.get(xApiRef)).toBe(x); expect(registry.get(yApiRef)).toBeUndefined(); @@ -125,7 +125,7 @@ describe('TestApiRegistry', () => { const x1 = { a: 'a' }; const x2 = { a: 's' }; const x3 = { a: 'd' }; - const registry = TestApiRegistry.with( + const registry = TestApiRegistry.from( [xApiRef, x1], [xApiRef, x2], [xApiRef, x3], diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 92be0ffd77..43774b7267 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -63,7 +63,7 @@ export class TestApiRegistry implements ApiHolder { * @public * @param apis - A list of pairs mapping an ApiRef to its respective implementation. */ - static with( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ) { return new TestApiRegistry( @@ -121,6 +121,6 @@ export const TestApiProvider = ({ children, }: TestApiProviderProps) => { return ( - + ); }; From 92f87a9a16acd865097677c2c203c8cd5fab8d0c Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Nov 2021 10:52:37 +0100 Subject: [PATCH 094/139] chore: fixing dockerode Signed-off-by: blam --- packages/backend-common/package.json | 2 +- packages/backend/package.json | 2 +- .../templates/default-app/packages/backend/package.json.hbs | 2 +- packages/techdocs-cli/package.json | 2 +- plugins/techdocs-backend/package.json | 2 +- yarn.lock | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index c46545c4d3..ce392a782b 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -39,7 +39,7 @@ "@lerna/project": "^4.0.0", "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", - "@types/dockerode": "^3.3.1", + "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "archiver": "^5.0.2", "aws-sdk": "^2.840.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index a05bdae692..7fdffc087f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -69,7 +69,7 @@ }, "devDependencies": { "@backstage/cli": "^0.8.2", - "@types/dockerode": "^3.3.1", + "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" }, diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 8fc193c911..8f2de6a8ae 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -43,7 +43,7 @@ }, "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", - "@types/dockerode": "^3.3.1", + "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" }, diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index ecdf488571..28903ccb9a 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -60,7 +60,7 @@ "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", "@backstage/techdocs-common": "^0.10.4", - "@types/dockerode": "^3.3.1", + "@types/dockerode": "^3.3.0", "commander": "^6.1.0", "dockerode": "^3.3.1", "fs-extra": "^9.0.1", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 1524a8300e..30be9c9063 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -53,7 +53,7 @@ "devDependencies": { "@backstage/cli": "^0.8.1", "@backstage/test-utils": "^0.1.20", - "@types/dockerode": "^3.3.1", + "@types/dockerode": "^3.3.0", "msw": "^0.35.0", "supertest": "^6.1.3" }, diff --git a/yarn.lock b/yarn.lock index 3188052baf..597ae5e487 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7074,7 +7074,7 @@ "@types/node" "*" "@types/ssh2" "*" -"@types/dockerode@^3.2.1", "@types/dockerode@^3.2.5": +"@types/dockerode@^3.2.5", "@types/dockerode@^3.3.0": version "3.3.0" resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz#d6afcc1de31e211ee900da3a48ef4f1a496cf05a" integrity sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg== @@ -13109,7 +13109,7 @@ docker-modem@^3.0.0: split-ca "^1.0.1" ssh2 "^1.4.0" -dockerode@^3.2.1, dockerode@^3.3.1: +dockerode@^3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz#74f66e239e092e7910e2beae6322d35c44b08cdc" integrity sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ== @@ -27256,7 +27256,7 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -testcontainers@^7.10.0: +testcontainers@^7.23.0: version "7.23.0" resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-7.23.0.tgz#7d73a5e219a970fb75ff6a23a28dace8b7f3f232" integrity sha512-90H1iijeIjOLp7WVNYKTNkM1sd+dlW5019ns45hSPcOET43WebEZQVJl8/Ag9vwSZD2mjomMum9a/EXk/st4sQ== From 17b143c87377d096dc185f9ce279980ade98df14 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Nov 2021 10:54:01 +0100 Subject: [PATCH 095/139] chore: fixing bad dependency ref Signed-off-by: blam --- packages/embedded-techdocs-app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/embedded-techdocs-app/package.json b/packages/embedded-techdocs-app/package.json index 7901d46fee..412fd80357 100644 --- a/packages/embedded-techdocs-app/package.json +++ b/packages/embedded-techdocs-app/package.json @@ -26,7 +26,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^089.0", + "@backstage/cli": "^0.8.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", From 69f294623e20751be93ad1d9901287acaad5352f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:26:01 +0000 Subject: [PATCH 096/139] chore(deps): bump url-parse from 1.4.7 to 1.5.3 in /microsite Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.3. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.3) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index f3834ac734..d6d6a23701 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -6502,9 +6502,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3: - version "1.4.7" - resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + version "1.5.3" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" + integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" From 6c15c54818c09b2c3939dd10ca98d6886b14c44a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:26:06 +0000 Subject: [PATCH 097/139] chore(deps): bump path-parse from 1.0.6 to 1.0.7 in /microsite Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7. - [Release notes](https://github.com/jbgutierrez/path-parse/releases) - [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7) --- updated-dependencies: - dependency-name: path-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index f3834ac734..7783755ff0 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -4815,9 +4815,9 @@ path-key@^2.0.0, path-key@^2.0.1: integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" From 475edb5bc593f609d94587ffc4f448c2ef989020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Nov 2021 10:58:42 +0100 Subject: [PATCH 098/139] move the BehaviorSubject init into the constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/unlucky-chefs-arrive.md | 5 +++ packages/core-app-api/src/lib/subjects.ts | 39 ++++++++++++----------- 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 .changeset/unlucky-chefs-arrive.md diff --git a/.changeset/unlucky-chefs-arrive.md b/.changeset/unlucky-chefs-arrive.md new file mode 100644 index 0000000000..4b1450b281 --- /dev/null +++ b/.changeset/unlucky-chefs-arrive.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +move the BehaviorSubject init into the constructor diff --git a/packages/core-app-api/src/lib/subjects.ts b/packages/core-app-api/src/lib/subjects.ts index 79c2c25675..e0789da92c 100644 --- a/packages/core-app-api/src/lib/subjects.ts +++ b/packages/core-app-api/src/lib/subjects.ts @@ -121,34 +121,37 @@ export class PublishSubject * * See http://reactivex.io/documentation/subject.html */ + export class BehaviorSubject implements Observable, ZenObservable.SubscriptionObserver { - private isClosed = false; + private isClosed: boolean; private currentValue: T; - private terminatingError?: Error; + private terminatingError: Error | undefined; + private readonly observable: Observable; constructor(value: T) { + this.isClosed = false; this.currentValue = value; - } - - private readonly observable = new ObservableImpl(subscriber => { - if (this.isClosed) { - if (this.terminatingError) { - subscriber.error(this.terminatingError); - } else { - subscriber.complete(); + this.terminatingError = undefined; + this.observable = new ObservableImpl(subscriber => { + if (this.isClosed) { + if (this.terminatingError) { + subscriber.error(this.terminatingError); + } else { + subscriber.complete(); + } + return () => {}; } - return () => {}; - } - subscriber.next(this.currentValue); + subscriber.next(this.currentValue); - this.subscribers.add(subscriber); - return () => { - this.subscribers.delete(subscriber); - }; - }); + this.subscribers.add(subscriber); + return () => { + this.subscribers.delete(subscriber); + }; + }); + } private readonly subscribers = new Set< ZenObservable.SubscriptionObserver From 0b8032bd3d3c3a14fb16870a3815176db7f7366a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:27:13 +0000 Subject: [PATCH 099/139] chore(deps): bump color-string from 1.5.4 to 1.6.0 in /microsite Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.6.0. - [Release notes](https://github.com/Qix-/color-string/releases) - [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md) - [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.6.0) --- updated-dependencies: - dependency-name: color-string dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index f3834ac734..52725f2f6f 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -1687,9 +1687,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" - integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== + version "1.6.0" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" + integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" From 4147f45b3e7d6d438685e5f51c90a319256a80c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:27:14 +0000 Subject: [PATCH 100/139] chore(deps): bump prismjs from 1.23.0 to 1.25.0 in /microsite Bumps [prismjs](https://github.com/PrismJS/prism) from 1.23.0 to 1.25.0. - [Release notes](https://github.com/PrismJS/prism/releases) - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md) - [Commits](https://github.com/PrismJS/prism/compare/v1.23.0...v1.25.0) --- updated-dependencies: - dependency-name: prismjs dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index f3834ac734..5d6f84684b 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -1624,15 +1624,6 @@ cli-width@^2.0.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== -clipboard@^2.0.0: - version "2.0.6" - resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" - integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== - dependencies: - good-listener "^1.2.2" - select "^1.1.2" - tiny-emitter "^2.0.0" - clone-response@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -2158,11 +2149,6 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegate@^3.1.2: - version "3.2.0" - resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" - integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3097,13 +3083,6 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" -good-listener@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" - integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= - dependencies: - delegate "^3.1.2" - got@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -5213,11 +5192,9 @@ prettier@^2.4.1: integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== prismjs@^1.22.0: - version "1.23.0" - resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33" - integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA== - optionalDependencies: - clipboard "^2.0.0" + version "1.25.0" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" + integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== process-nextick-args@~2.0.0: version "2.0.1" @@ -5709,11 +5686,6 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" -select@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" - integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= - semver-regex@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" @@ -6250,11 +6222,6 @@ timsort@^0.3.0: resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tiny-emitter@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" - integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - tiny-lr@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" From 7ff56c23309ae0da66f3561aa425615ba27e3dee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 18 Nov 2021 11:28:26 +0100 Subject: [PATCH 101/139] test-utils: doc + API report fixup Signed-off-by: Patrik Oldsberg --- packages/test-utils/api-report.md | 3 +-- packages/test-utils/src/testUtils/TestApiProvider.tsx | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index a1f22a7a64..83c209122d 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -191,11 +191,10 @@ export type TestApiProviderProps = { // @public export class TestApiRegistry implements ApiHolder { - // (undocumented) - get(api: ApiRef): T | undefined; static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ): TestApiRegistry; + get(api: ApiRef): T | undefined; } // @public diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 43774b7267..4408fc5b36 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -73,7 +73,11 @@ export class TestApiRegistry implements ApiHolder { private constructor(private readonly apis: Map) {} - /** {@inheritdoc @backstage/core-plugin-api#ApiHolder.get} */ + /** + * Returns an implementation of the API. + * + * @public + */ get(api: ApiRef): T | undefined { return this.apis.get(api.id) as T | undefined; } From 5ca190c4314a7cb7b03931024f678dd35523dd52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:36:19 +0000 Subject: [PATCH 102/139] chore(deps): bump set-getter from 0.1.0 to 0.1.1 in /microsite Bumps [set-getter](https://github.com/doowb/set-getter) from 0.1.0 to 0.1.1. - [Release notes](https://github.com/doowb/set-getter/releases) - [Commits](https://github.com/doowb/set-getter/commits/0.1.1) --- updated-dependencies: - dependency-name: set-getter dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index d6d6a23701..be965fe848 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -5766,9 +5766,9 @@ serve-static@1.14.1: send "0.17.1" set-getter@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" - integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y= + version "0.1.1" + resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102" + integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw== dependencies: to-object-path "^0.3.0" From 08dc441019ac2ae9054595341327c88f47b1f2de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:37:27 +0000 Subject: [PATCH 103/139] chore(deps): bump postcss from 7.0.35 to 7.0.39 in /microsite Bumps [postcss](https://github.com/postcss/postcss) from 7.0.35 to 7.0.39. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/7.0.39/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/7.0.35...7.0.39) --- updated-dependencies: - dependency-name: postcss dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 17b22dafee..186f185fd4 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -4829,6 +4829,11 @@ performance-now@^2.1.0: resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5168,13 +5173,12 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.32: - version "7.0.35" - resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" - integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== dependencies: - chalk "^2.4.2" + picocolors "^0.2.1" source-map "^0.6.1" - supports-color "^6.1.0" prepend-http@^1.0.1: version "1.0.4" @@ -6122,13 +6126,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" From 3b06282579a83909694088a611c4b989bc75c77d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:48:52 +0000 Subject: [PATCH 104/139] chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 in /microsite Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9) --- updated-dependencies: - dependency-name: hosted-git-info dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index c75c0b1069..7af92f3d16 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -3255,9 +3255,9 @@ highlight.js@^9.16.2: integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hsl-regex@^1.0.0: version "1.0.0" From d3248fb8c9577f3ad4268d206c8ad5555171eddd Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 18 Nov 2021 11:52:40 +0100 Subject: [PATCH 105/139] add techdocs search capabilities to default app Signed-off-by: Emma Indal --- .../app/src/components/search/SearchPage.tsx | 15 +++++++++++++++ .../packages/backend/src/plugins/search.ts | 10 +++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx index 7b3c2b29d4..50ffbadb76 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx @@ -2,10 +2,13 @@ import React from 'react'; import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; +import { DocsResultListItem } from '@backstage/plugin-techdocs'; + import { SearchBar, SearchFilter, SearchResult, + SearchType, DefaultResultListItem, } from '@backstage/plugin-search'; import { Content, Header, Page } from '@backstage/core-components'; @@ -39,6 +42,11 @@ const SearchPage = () => { + { result={document} /> ); + case 'techdocs': + return ( + + ); default: return ( Date: Thu, 18 Nov 2021 12:39:48 +0100 Subject: [PATCH 106/139] add changeset Signed-off-by: Emma Indal --- .changeset/large-pears-agree.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/large-pears-agree.md diff --git a/.changeset/large-pears-agree.md b/.changeset/large-pears-agree.md new file mode 100644 index 0000000000..60f755eff0 --- /dev/null +++ b/.changeset/large-pears-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type From 99bf179ccfe13b2c91e890c683443ea2a1c1773f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Nov 2021 11:52:08 +0000 Subject: [PATCH 107/139] Version Packages --- .changeset/big-cougars-glow.md | 5 -- .changeset/big-months-float.md | 6 -- .changeset/blue-bikes-explode.md | 14 ---- .changeset/cost-insights-two-crabs-evolve.md | 5 -- .changeset/cyan-bottles-hug.md | 8 -- .changeset/early-cobras-explode.md | 5 -- .changeset/eight-months-agree.md | 19 ----- .changeset/empty-dots-attend.md | 8 -- .changeset/fluffy-moles-deny.md | 42 ---------- .changeset/forty-ligers-protect.md | 5 -- .changeset/fresh-zebras-hug.md | 5 -- .changeset/giant-drinks-wave.md | 5 -- .changeset/happy-rice-tickle.md | 5 -- .changeset/healthy-kids-mate.md | 11 --- .changeset/honest-shrimps-dance.md | 9 -- .changeset/hot-walls-fail.md | 13 --- .changeset/hungry-wombats-happen.md | 6 -- .changeset/khaki-rice-kick.md | 6 -- .changeset/little-news-retire.md | 6 -- .changeset/little-numbers-thank.md | 5 -- .changeset/lucky-ads-shout.md | 5 -- .changeset/many-sloths-cross.md | 5 -- .changeset/metal-donuts-cross.md | 8 -- .changeset/moody-snails-admire.md | 5 -- .changeset/nasty-impalas-travel.md | 5 -- .changeset/new-cobras-fly.md | 5 -- .changeset/nine-bananas-mate.md | 6 -- .changeset/ninety-grapes-love.md | 6 -- .changeset/ninety-spies-prove.md | 5 -- .changeset/orange-experts-approve.md | 5 -- .changeset/pretty-moons-drive.md | 5 -- .changeset/proud-bottles-cheat.md | 5 -- .changeset/rare-lemons-boil.md | 5 -- .changeset/rich-pillows-cough.md | 12 --- .changeset/rotten-clouds-kick.md | 5 -- .changeset/search-serious-hornets-design.md | 5 -- .changeset/sharp-meals-fetch.md | 5 -- .changeset/sharp-moons-jog.md | 5 -- .changeset/shiny-starfishes-float.md | 5 -- .changeset/silent-taxis-tan.md | 5 -- .changeset/slow-moles-act.md | 5 -- .changeset/sour-cameras-hide.md | 5 -- .changeset/spicy-rice-build.md | 5 -- .changeset/sweet-suits-sit.md | 5 -- .changeset/tame-buckets-move.md | 7 -- .../techdocs-chatt-months-report-too.md | 5 -- .changeset/techdocs-chatty-months-report.md | 5 -- .changeset/techdocs-famous-donuts-warn.md | 5 -- .changeset/techdocs-orange-cougars-relax.md | 6 -- .changeset/techdocs-real-geese-dress.md | 5 -- .changeset/techdocs-sharp-knives-unite.md | 5 -- .changeset/techdocs-silver-plums-speak.md | 5 -- .changeset/ten-planes-remember.md | 8 -- .changeset/tender-gorillas-peel.md | 5 -- .changeset/tidy-beans-reflect.md | 5 -- .changeset/twenty-swans-matter.md | 9 -- .changeset/unlucky-chefs-arrive.md | 5 -- .changeset/violet-panthers-care.md | 5 -- .changeset/yellow-deers-act.md | 5 -- .changeset/yellow-falcons-whisper.md | 5 -- packages/app-defaults/CHANGELOG.md | 10 +++ packages/app-defaults/package.json | 12 +-- packages/app/CHANGELOG.md | 45 ++++++++++ packages/app/package.json | 82 +++++++++---------- packages/backend-common/CHANGELOG.md | 24 ++++++ packages/backend-common/package.json | 10 +-- packages/backend-tasks/package.json | 6 +- packages/backend-test-utils/CHANGELOG.md | 9 ++ packages/backend-test-utils/package.json | 8 +- packages/backend/package.json | 26 +++--- packages/catalog-client/CHANGELOG.md | 8 ++ packages/catalog-client/package.json | 6 +- packages/catalog-model/CHANGELOG.md | 6 ++ packages/catalog-model/package.json | 4 +- packages/cli-common/CHANGELOG.md | 8 ++ packages/cli-common/package.json | 2 +- packages/cli/CHANGELOG.md | 23 ++++++ packages/cli/package.json | 18 ++-- packages/codemods/CHANGELOG.md | 10 +++ packages/codemods/package.json | 4 +- packages/config-loader/CHANGELOG.md | 21 +++++ packages/config-loader/package.json | 4 +- packages/core-app-api/CHANGELOG.md | 19 +++++ packages/core-app-api/package.json | 12 +-- packages/core-components/CHANGELOG.md | 10 +++ packages/core-components/package.json | 10 +-- packages/core-plugin-api/CHANGELOG.md | 13 +++ packages/core-plugin-api/package.json | 8 +- packages/create-app/CHANGELOG.md | 27 ++++++ packages/create-app/package.json | 4 +- packages/dev-utils/CHANGELOG.md | 16 ++++ packages/dev-utils/package.json | 20 ++--- packages/embedded-techdocs-app/CHANGELOG.md | 17 ++++ packages/embedded-techdocs-app/package.json | 24 +++--- packages/errors/package.json | 2 +- packages/integration-react/CHANGELOG.md | 8 ++ packages/integration-react/package.json | 12 +-- packages/integration/package.json | 6 +- packages/search-common/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 12 +++ packages/techdocs-cli/package.json | 12 +-- packages/techdocs-common/CHANGELOG.md | 10 +++ packages/techdocs-common/package.json | 8 +- packages/test-utils/CHANGELOG.md | 10 +++ packages/test-utils/package.json | 8 +- packages/theme/package.json | 2 +- packages/types/package.json | 2 +- packages/version-bridge/package.json | 2 +- plugins/allure/CHANGELOG.md | 11 +++ plugins/allure/package.json | 18 ++-- plugins/analytics-module-ga/CHANGELOG.md | 8 ++ plugins/analytics-module-ga/package.json | 14 ++-- plugins/api-docs/CHANGELOG.md | 12 +++ plugins/api-docs/package.json | 20 ++--- plugins/app-backend/CHANGELOG.md | 8 ++ plugins/app-backend/package.json | 8 +- plugins/auth-backend/CHANGELOG.md | 11 +++ plugins/auth-backend/package.json | 12 +-- plugins/azure-devops-backend/CHANGELOG.md | 9 ++ plugins/azure-devops-backend/package.json | 8 +- plugins/azure-devops-common/CHANGELOG.md | 6 ++ plugins/azure-devops-common/package.json | 4 +- plugins/azure-devops/CHANGELOG.md | 14 ++++ plugins/azure-devops/package.json | 20 ++--- plugins/badges-backend/package.json | 8 +- plugins/badges/CHANGELOG.md | 11 +++ plugins/badges/package.json | 18 ++-- plugins/bazaar-backend/CHANGELOG.md | 10 +++ plugins/bazaar-backend/package.json | 8 +- plugins/bazaar/CHANGELOG.md | 14 ++++ plugins/bazaar/package.json | 18 ++-- plugins/bitrise/CHANGELOG.md | 10 +++ plugins/bitrise/package.json | 18 ++-- .../catalog-backend-module-ldap/package.json | 6 +- .../package.json | 10 +-- plugins/catalog-backend/CHANGELOG.md | 16 ++++ plugins/catalog-backend/package.json | 14 ++-- plugins/catalog-graph/CHANGELOG.md | 12 +++ plugins/catalog-graph/package.json | 20 ++--- plugins/catalog-graphql/package.json | 6 +- plugins/catalog-import/CHANGELOG.md | 13 +++ plugins/catalog-import/package.json | 22 ++--- plugins/catalog-react/CHANGELOG.md | 12 +++ plugins/catalog-react/package.json | 16 ++-- plugins/catalog/CHANGELOG.md | 15 ++++ plugins/catalog/package.json | 22 ++--- plugins/circleci/CHANGELOG.md | 11 +++ plugins/circleci/package.json | 18 ++-- plugins/cloudbuild/CHANGELOG.md | 11 +++ plugins/cloudbuild/package.json | 18 ++-- plugins/code-coverage-backend/package.json | 8 +- plugins/code-coverage/CHANGELOG.md | 11 +++ plugins/code-coverage/package.json | 18 ++-- plugins/config-schema/CHANGELOG.md | 9 ++ plugins/config-schema/package.json | 14 ++-- plugins/cost-insights/CHANGELOG.md | 10 +++ plugins/cost-insights/package.json | 14 ++-- plugins/explore-react/CHANGELOG.md | 7 ++ plugins/explore-react/package.json | 10 +-- plugins/explore/CHANGELOG.md | 12 +++ plugins/explore/package.json | 20 ++--- plugins/firehydrant/CHANGELOG.md | 10 +++ plugins/firehydrant/package.json | 16 ++-- plugins/fossa/CHANGELOG.md | 10 +++ plugins/fossa/package.json | 18 ++-- plugins/gcp-projects/CHANGELOG.md | 25 ++++++ plugins/gcp-projects/package.json | 14 ++-- plugins/git-release-manager/CHANGELOG.md | 9 ++ plugins/git-release-manager/package.json | 14 ++-- plugins/github-actions/CHANGELOG.md | 11 +++ plugins/github-actions/package.json | 18 ++-- plugins/github-deployments/CHANGELOG.md | 11 +++ plugins/github-deployments/package.json | 20 ++--- plugins/gitops-profiles/CHANGELOG.md | 9 ++ plugins/gitops-profiles/package.json | 14 ++-- plugins/graphiql/CHANGELOG.md | 8 ++ plugins/graphiql/package.json | 14 ++-- plugins/graphql-backend/package.json | 4 +- plugins/home/CHANGELOG.md | 9 ++ plugins/home/package.json | 14 ++-- plugins/ilert/CHANGELOG.md | 11 +++ plugins/ilert/package.json | 18 ++-- plugins/jenkins-backend/package.json | 8 +- plugins/jenkins/CHANGELOG.md | 11 +++ plugins/jenkins/package.json | 18 ++-- plugins/kafka-backend/CHANGELOG.md | 9 ++ plugins/kafka-backend/package.json | 8 +- plugins/kafka/CHANGELOG.md | 11 +++ plugins/kafka/package.json | 18 ++-- plugins/kubernetes-backend/package.json | 6 +- plugins/kubernetes-common/package.json | 4 +- plugins/kubernetes/CHANGELOG.md | 11 +++ plugins/kubernetes/package.json | 18 ++-- plugins/lighthouse/CHANGELOG.md | 11 +++ plugins/lighthouse/package.json | 18 ++-- plugins/newrelic/CHANGELOG.md | 9 ++ plugins/newrelic/package.json | 14 ++-- plugins/org/CHANGELOG.md | 10 +++ plugins/org/package.json | 18 ++-- plugins/pagerduty/CHANGELOG.md | 11 +++ plugins/pagerduty/package.json | 18 ++-- plugins/permission-common/package.json | 2 +- plugins/proxy-backend/package.json | 4 +- plugins/rollbar-backend/package.json | 4 +- plugins/rollbar/CHANGELOG.md | 11 +++ plugins/rollbar/package.json | 18 ++-- .../package.json | 6 +- .../package.json | 6 +- plugins/scaffolder-backend/CHANGELOG.md | 12 +++ plugins/scaffolder-backend/package.json | 14 ++-- plugins/scaffolder-common/package.json | 4 +- plugins/scaffolder/CHANGELOG.md | 14 ++++ plugins/scaffolder/package.json | 22 ++--- .../package.json | 4 +- plugins/search-backend-module-pg/package.json | 6 +- plugins/search-backend-node/package.json | 4 +- plugins/search-backend/package.json | 4 +- plugins/search/CHANGELOG.md | 12 +++ plugins/search/package.json | 18 ++-- plugins/sentry/CHANGELOG.md | 11 +++ plugins/sentry/package.json | 18 ++-- plugins/shortcuts/CHANGELOG.md | 9 ++ plugins/shortcuts/package.json | 14 ++-- plugins/sonarqube/CHANGELOG.md | 10 +++ plugins/sonarqube/package.json | 18 ++-- plugins/splunk-on-call/CHANGELOG.md | 11 +++ plugins/splunk-on-call/package.json | 18 ++-- .../package.json | 4 +- plugins/tech-insights-backend/CHANGELOG.md | 15 ++++ plugins/tech-insights-backend/package.json | 12 +-- plugins/tech-insights-common/package.json | 2 +- plugins/tech-insights-node/package.json | 4 +- plugins/tech-radar/CHANGELOG.md | 9 ++ plugins/tech-radar/package.json | 14 ++-- plugins/techdocs-backend/CHANGELOG.md | 12 +++ plugins/techdocs-backend/package.json | 14 ++-- plugins/techdocs/CHANGELOG.md | 17 ++++ plugins/techdocs/package.json | 24 +++--- plugins/todo-backend/package.json | 8 +- plugins/todo/CHANGELOG.md | 11 +++ plugins/todo/package.json | 18 ++-- plugins/user-settings/CHANGELOG.md | 10 +++ plugins/user-settings/package.json | 14 ++-- plugins/xcmetrics/CHANGELOG.md | 9 ++ plugins/xcmetrics/package.json | 14 ++-- yarn.lock | 20 ++++- 246 files changed, 1632 insertions(+), 1086 deletions(-) delete mode 100644 .changeset/big-cougars-glow.md delete mode 100644 .changeset/big-months-float.md delete mode 100644 .changeset/blue-bikes-explode.md delete mode 100644 .changeset/cost-insights-two-crabs-evolve.md delete mode 100644 .changeset/cyan-bottles-hug.md delete mode 100644 .changeset/early-cobras-explode.md delete mode 100644 .changeset/eight-months-agree.md delete mode 100644 .changeset/empty-dots-attend.md delete mode 100644 .changeset/fluffy-moles-deny.md delete mode 100644 .changeset/forty-ligers-protect.md delete mode 100644 .changeset/fresh-zebras-hug.md delete mode 100644 .changeset/giant-drinks-wave.md delete mode 100644 .changeset/happy-rice-tickle.md delete mode 100644 .changeset/healthy-kids-mate.md delete mode 100644 .changeset/honest-shrimps-dance.md delete mode 100644 .changeset/hot-walls-fail.md delete mode 100644 .changeset/hungry-wombats-happen.md delete mode 100644 .changeset/khaki-rice-kick.md delete mode 100644 .changeset/little-news-retire.md delete mode 100644 .changeset/little-numbers-thank.md delete mode 100644 .changeset/lucky-ads-shout.md delete mode 100644 .changeset/many-sloths-cross.md delete mode 100644 .changeset/metal-donuts-cross.md delete mode 100644 .changeset/moody-snails-admire.md delete mode 100644 .changeset/nasty-impalas-travel.md delete mode 100644 .changeset/new-cobras-fly.md delete mode 100644 .changeset/nine-bananas-mate.md delete mode 100644 .changeset/ninety-grapes-love.md delete mode 100644 .changeset/ninety-spies-prove.md delete mode 100644 .changeset/orange-experts-approve.md delete mode 100644 .changeset/pretty-moons-drive.md delete mode 100644 .changeset/proud-bottles-cheat.md delete mode 100644 .changeset/rare-lemons-boil.md delete mode 100644 .changeset/rich-pillows-cough.md delete mode 100644 .changeset/rotten-clouds-kick.md delete mode 100644 .changeset/search-serious-hornets-design.md delete mode 100644 .changeset/sharp-meals-fetch.md delete mode 100644 .changeset/sharp-moons-jog.md delete mode 100644 .changeset/shiny-starfishes-float.md delete mode 100644 .changeset/silent-taxis-tan.md delete mode 100644 .changeset/slow-moles-act.md delete mode 100644 .changeset/sour-cameras-hide.md delete mode 100644 .changeset/spicy-rice-build.md delete mode 100644 .changeset/sweet-suits-sit.md delete mode 100644 .changeset/tame-buckets-move.md delete mode 100644 .changeset/techdocs-chatt-months-report-too.md delete mode 100644 .changeset/techdocs-chatty-months-report.md delete mode 100644 .changeset/techdocs-famous-donuts-warn.md delete mode 100644 .changeset/techdocs-orange-cougars-relax.md delete mode 100644 .changeset/techdocs-real-geese-dress.md delete mode 100644 .changeset/techdocs-sharp-knives-unite.md delete mode 100644 .changeset/techdocs-silver-plums-speak.md delete mode 100644 .changeset/ten-planes-remember.md delete mode 100644 .changeset/tender-gorillas-peel.md delete mode 100644 .changeset/tidy-beans-reflect.md delete mode 100644 .changeset/twenty-swans-matter.md delete mode 100644 .changeset/unlucky-chefs-arrive.md delete mode 100644 .changeset/violet-panthers-care.md delete mode 100644 .changeset/yellow-deers-act.md delete mode 100644 .changeset/yellow-falcons-whisper.md create mode 100644 packages/app-defaults/CHANGELOG.md create mode 100644 packages/embedded-techdocs-app/CHANGELOG.md create mode 100644 plugins/bazaar-backend/CHANGELOG.md create mode 100644 plugins/tech-insights-backend/CHANGELOG.md diff --git a/.changeset/big-cougars-glow.md b/.changeset/big-cougars-glow.md deleted file mode 100644 index d1630905db..0000000000 --- a/.changeset/big-cougars-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/dev-utils': patch ---- - -Add theme switcher to sidebar of dev app. diff --git a/.changeset/big-months-float.md b/.changeset/big-months-float.md deleted file mode 100644 index 9acb7002e7..0000000000 --- a/.changeset/big-months-float.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Remove the "View Api" icon in the AboutCard, as the information is misleading for some users and is -duplicated in the tabs above. diff --git a/.changeset/blue-bikes-explode.md b/.changeset/blue-bikes-explode.md deleted file mode 100644 index 1c10a08a46..0000000000 --- a/.changeset/blue-bikes-explode.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Update `loadConfig` to return `LoadConfigResult` instead of an array of `AppConfig`. - -This function is primarily used internally by other config loaders like `loadBackendConfig` which means no changes are required for most users. - -If you use `loadConfig` directly you will need to update your usage from: - -```diff -- const appConfigs = await loadConfig(options) -+ const { appConfigs } = await loadConfig(options) -``` diff --git a/.changeset/cost-insights-two-crabs-evolve.md b/.changeset/cost-insights-two-crabs-evolve.md deleted file mode 100644 index 8a932ea59c..0000000000 --- a/.changeset/cost-insights-two-crabs-evolve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -Fixed generation of sample data in the example Cost Insights client diff --git a/.changeset/cyan-bottles-hug.md b/.changeset/cyan-bottles-hug.md deleted file mode 100644 index 7c2083c4ff..0000000000 --- a/.changeset/cyan-bottles-hug.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/cli': patch -'@backstage/cli-common': patch ---- - -Keep backstage.json in sync - -The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present. diff --git a/.changeset/early-cobras-explode.md b/.changeset/early-cobras-explode.md deleted file mode 100644 index 34b4cb0e98..0000000000 --- a/.changeset/early-cobras-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': patch ---- - -Update to the right version of @backstage/errors diff --git a/.changeset/eight-months-agree.md b/.changeset/eight-months-agree.md deleted file mode 100644 index 0f117d8a40..0000000000 --- a/.changeset/eight-months-agree.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -'@backstage/plugin-gcp-projects': patch ---- - -UI updates to GCP-projects plugin - -Adds the following to the project list page: - -- pagination -- filtering -- sorting -- rows per page -- show/hide columns - -Makes breadcrumb a link back to project list for the project details and new project views. - -In project list page, updates New project button to use RouterLink instead of `href` to avoid login prompt. - -In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. diff --git a/.changeset/empty-dots-attend.md b/.changeset/empty-dots-attend.md deleted file mode 100644 index 3903dfa22b..0000000000 --- a/.changeset/empty-dots-attend.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/plugin-scaffolder': patch ---- - -Update the json-schema dependency version. diff --git a/.changeset/fluffy-moles-deny.md b/.changeset/fluffy-moles-deny.md deleted file mode 100644 index 4b4abc36fa..0000000000 --- a/.changeset/fluffy-moles-deny.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -'@backstage/plugin-allure': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bazaar': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-catalog-graph': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-catalog-react': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-xcmetrics': patch ---- - -Refactor out the deprecated path and icon from RouteRefs diff --git a/.changeset/forty-ligers-protect.md b/.changeset/forty-ligers-protect.md deleted file mode 100644 index e3e46fb7f3..0000000000 --- a/.changeset/forty-ligers-protect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -remove double config dep diff --git a/.changeset/fresh-zebras-hug.md b/.changeset/fresh-zebras-hug.md deleted file mode 100644 index c295dc2287..0000000000 --- a/.changeset/fresh-zebras-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kafka-backend': patch ---- - -Update Kafka configuration types diff --git a/.changeset/giant-drinks-wave.md b/.changeset/giant-drinks-wave.md deleted file mode 100644 index ecf4c8fbdd..0000000000 --- a/.changeset/giant-drinks-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/dev-utils': patch ---- - -Migrated to using `@backstage/app-defaults`. diff --git a/.changeset/happy-rice-tickle.md b/.changeset/happy-rice-tickle.md deleted file mode 100644 index 1e39dc2e9c..0000000000 --- a/.changeset/happy-rice-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Added the `isDatabaseConflictError` function. diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md deleted file mode 100644 index 10e8222a15..0000000000 --- a/.changeset/healthy-kids-mate.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Create backstage.json file - -`@backstage/create-app` will create a new `backstage.json` file. At this point, the file will contain a `version` property, representing the version of `@backstage/create-app` used for creating the application. If the backstage's application has been bootstrapped using an older version of `@backstage/create-app`, the `backstage.json` file can be created and kept in sync, together with all the changes of the latest version of backstage, by running the following script: - -```bash -yarn backstage-cli versions:bump -``` diff --git a/.changeset/honest-shrimps-dance.md b/.changeset/honest-shrimps-dance.md deleted file mode 100644 index 8c7852b96b..0000000000 --- a/.changeset/honest-shrimps-dance.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/backend-test-utils': patch -'@backstage/create-app': patch -'@techdocs/cli': patch -'@backstage/plugin-techdocs-backend': patch ---- - -Bumping minimum requirements for `dockerode` and `testcontainers` diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md deleted file mode 100644 index 3728e10ff6..0000000000 --- a/.changeset/hot-walls-fail.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future. - -To migrate an existing application, add the latest version of `@backstage/app-defaults` as a dependency in `packages/app/package.json`, and make the following change to `packages/app/src/App.tsx`: - -```diff --import { createApp, FlatRoutes } from '@backstage/core-app-api'; -+import { createApp } from '@backstage/app-defaults'; -+import { FlatRoutes } from '@backstage/core-app-api'; -``` diff --git a/.changeset/hungry-wombats-happen.md b/.changeset/hungry-wombats-happen.md deleted file mode 100644 index 018e7ae9e8..0000000000 --- a/.changeset/hungry-wombats-happen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch ---- - -Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs. diff --git a/.changeset/khaki-rice-kick.md b/.changeset/khaki-rice-kick.md deleted file mode 100644 index 54819b855a..0000000000 --- a/.changeset/khaki-rice-kick.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-user-settings': patch ---- - -Add Props Icon for Sidebar Item SidebarSearchField and Settings diff --git a/.changeset/little-news-retire.md b/.changeset/little-news-retire.md deleted file mode 100644 index 4304cf2d19..0000000000 --- a/.changeset/little-news-retire.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-bazaar': patch -'@backstage/plugin-bazaar-backend': patch ---- - -A Bazaar project has been extended with the following fields: size, start date (optional), end date (optional) and a responsible person. diff --git a/.changeset/little-numbers-thank.md b/.changeset/little-numbers-thank.md deleted file mode 100644 index f444a904c0..0000000000 --- a/.changeset/little-numbers-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Changing the `Header` styles to use more theme variables. With this the title `font-size` will not change on resizing the window. diff --git a/.changeset/lucky-ads-shout.md b/.changeset/lucky-ads-shout.md deleted file mode 100644 index 73063bb911..0000000000 --- a/.changeset/lucky-ads-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Add deprecation warnings around `title` `icon` and `path` as they are no longer controlled when creating `routeRefs` diff --git a/.changeset/many-sloths-cross.md b/.changeset/many-sloths-cross.md deleted file mode 100644 index 07cb6d2f61..0000000000 --- a/.changeset/many-sloths-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Refactor and add regression tests for create-app tasks diff --git a/.changeset/metal-donuts-cross.md b/.changeset/metal-donuts-cross.md deleted file mode 100644 index 6c2699ed61..0000000000 --- a/.changeset/metal-donuts-cross.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -This fixes a bug where locations couldn't be added unless the processing engine is started. -It's now possible to run the catalog backend without starting the processing engine and still allowing locations registrations. - -This is done by refactor the `EntityProvider.connect` to happen outside the engine. diff --git a/.changeset/moody-snails-admire.md b/.changeset/moody-snails-admire.md deleted file mode 100644 index 7622546989..0000000000 --- a/.changeset/moody-snails-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': minor ---- - -Tweaked style insertion logic to make sure that JSS stylesheets always receive the highest priority. diff --git a/.changeset/nasty-impalas-travel.md b/.changeset/nasty-impalas-travel.md deleted file mode 100644 index f6eb321628..0000000000 --- a/.changeset/nasty-impalas-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Paths can be specified in backend.reading.allow to further restrict allowed targets diff --git a/.changeset/new-cobras-fly.md b/.changeset/new-cobras-fly.md deleted file mode 100644 index f39f1b0fbb..0000000000 --- a/.changeset/new-cobras-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-azure-devops': patch ---- - -Added entity view for Azure Repo Pull Requests diff --git a/.changeset/nine-bananas-mate.md b/.changeset/nine-bananas-mate.md deleted file mode 100644 index e119eb722f..0000000000 --- a/.changeset/nine-bananas-mate.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-app-api': patch -'@backstage/test-utils': patch ---- - -Migrated to using new `ErrorApiError` and `ErrorApiErrorContext` names. diff --git a/.changeset/ninety-grapes-love.md b/.changeset/ninety-grapes-love.md deleted file mode 100644 index 9143bddd30..0000000000 --- a/.changeset/ninety-grapes-love.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-auth-backend': patch ---- - -Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken diff --git a/.changeset/ninety-spies-prove.md b/.changeset/ninety-spies-prove.md deleted file mode 100644 index 509aedd66f..0000000000 --- a/.changeset/ninety-spies-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Skip empty file names when scaffolding with nunjucks diff --git a/.changeset/orange-experts-approve.md b/.changeset/orange-experts-approve.md deleted file mode 100644 index 13d1b022da..0000000000 --- a/.changeset/orange-experts-approve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Adjust entity query construction to ensure sub-queries are always isolated from one another. diff --git a/.changeset/pretty-moons-drive.md b/.changeset/pretty-moons-drive.md deleted file mode 100644 index 51dc69332e..0000000000 --- a/.changeset/pretty-moons-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Adding config prop `pluginDivisionMode` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. diff --git a/.changeset/proud-bottles-cheat.md b/.changeset/proud-bottles-cheat.md deleted file mode 100644 index 0c724352c7..0000000000 --- a/.changeset/proud-bottles-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Add `AbortSignal` support to `UrlReader` diff --git a/.changeset/rare-lemons-boil.md b/.changeset/rare-lemons-boil.md deleted file mode 100644 index b882ab0b91..0000000000 --- a/.changeset/rare-lemons-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -Deprecated the `BackstagePluginWithAnyOutput` type. diff --git a/.changeset/rich-pillows-cough.md b/.changeset/rich-pillows-cough.md deleted file mode 100644 index 959eb69214..0000000000 --- a/.changeset/rich-pillows-cough.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Make sure that server builder `start()` propagates errors (such as failing to bind to the required port) properly and doesn't resolve the promise prematurely. - -After this change, the backend logger will be able to actually capture the error as it happens: - -``` -2021-11-11T10:54:21.334Z backstage info Initializing http server -2021-11-11T10:54:21.335Z backstage error listen EADDRINUSE: address already in use :::7000 code=EADDRINUSE errno=-48 syscall=listen address=:: port=7000 -``` diff --git a/.changeset/rotten-clouds-kick.md b/.changeset/rotten-clouds-kick.md deleted file mode 100644 index 2c4772c6e5..0000000000 --- a/.changeset/rotten-clouds-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -Start exporting and marking several types as public to address errors in the API report. diff --git a/.changeset/search-serious-hornets-design.md b/.changeset/search-serious-hornets-design.md deleted file mode 100644 index 638f8ae73b..0000000000 --- a/.changeset/search-serious-hornets-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search': patch ---- - -Smaller UX improvements to search components such as optional autoFocus prop to SearchBar components, decreased debounce value and closing modal on link click of SearchModal, terminology updates of SearchResultPager. diff --git a/.changeset/sharp-meals-fetch.md b/.changeset/sharp-meals-fetch.md deleted file mode 100644 index 1d3df65ba4..0000000000 --- a/.changeset/sharp-meals-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': minor ---- - -Removed the unused `UserFlags` type. diff --git a/.changeset/sharp-moons-jog.md b/.changeset/sharp-moons-jog.md deleted file mode 100644 index d3329af08e..0000000000 --- a/.changeset/sharp-moons-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Deprecated the `theme` property on `AppTheme`, replacing it with `Provider`. See https://backstage.io/docs/api/deprecations#app-theme for more details. diff --git a/.changeset/shiny-starfishes-float.md b/.changeset/shiny-starfishes-float.md deleted file mode 100644 index 0673791375..0000000000 --- a/.changeset/shiny-starfishes-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added a scaffolder backend module template for the `create` command. diff --git a/.changeset/silent-taxis-tan.md b/.changeset/silent-taxis-tan.md deleted file mode 100644 index 286ef40a4c..0000000000 --- a/.changeset/silent-taxis-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': minor ---- - -Remove exports of unused types(`RouteOptions` and `RoutePath`). diff --git a/.changeset/slow-moles-act.md b/.changeset/slow-moles-act.md deleted file mode 100644 index ab920bfa5d..0000000000 --- a/.changeset/slow-moles-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/test-utils': patch ---- - -Migrated to using `createSpecializedApp`. diff --git a/.changeset/sour-cameras-hide.md b/.changeset/sour-cameras-hide.md deleted file mode 100644 index a31e7a13c3..0000000000 --- a/.changeset/sour-cameras-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fixed a bug where calling `backstage-cli backend:bundle --build-dependencies` with no dependencies to be built would cause all monorepo packages to be built instead. diff --git a/.changeset/spicy-rice-build.md b/.changeset/spicy-rice-build.md deleted file mode 100644 index c0322d778a..0000000000 --- a/.changeset/spicy-rice-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Do not redact empty or one-character strings. These imply that it's just a test or local dev, and unnecessarily ruin the log output. diff --git a/.changeset/sweet-suits-sit.md b/.changeset/sweet-suits-sit.md deleted file mode 100644 index b6f7fbadf5..0000000000 --- a/.changeset/sweet-suits-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-shortcuts': patch ---- - -Add tooltip to display shortcut title on hover. This should improve the readability of shortcuts with long names, which are cutoff by ellipsis. diff --git a/.changeset/tame-buckets-move.md b/.changeset/tame-buckets-move.md deleted file mode 100644 index f7ee3deb90..0000000000 --- a/.changeset/tame-buckets-move.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-azure-devops-common': minor ---- - -Improved Date handling for the Azure DevOps set of plugins by using strings and letting the frontend handle the conversion to DateTime diff --git a/.changeset/techdocs-chatt-months-report-too.md b/.changeset/techdocs-chatt-months-report-too.md deleted file mode 100644 index 612a4506a5..0000000000 --- a/.changeset/techdocs-chatt-months-report-too.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Fixed entity triplet case handling for certain locales. diff --git a/.changeset/techdocs-chatty-months-report.md b/.changeset/techdocs-chatty-months-report.md deleted file mode 100644 index 7d3fa773bf..0000000000 --- a/.changeset/techdocs-chatty-months-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Added OpenStack Swift case migration support. diff --git a/.changeset/techdocs-famous-donuts-warn.md b/.changeset/techdocs-famous-donuts-warn.md deleted file mode 100644 index 2fd9346ee2..0000000000 --- a/.changeset/techdocs-famous-donuts-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@techdocs/cli': patch ---- - -Add support for specifying bucketRootPath for AWS and GCS publishers diff --git a/.changeset/techdocs-orange-cougars-relax.md b/.changeset/techdocs-orange-cougars-relax.md deleted file mode 100644 index 376de6a0e5..0000000000 --- a/.changeset/techdocs-orange-cougars-relax.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/techdocs-common': patch -'@backstage/plugin-techdocs-backend': patch ---- - -Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets diff --git a/.changeset/techdocs-real-geese-dress.md b/.changeset/techdocs-real-geese-dress.md deleted file mode 100644 index 0d5f1978f9..0000000000 --- a/.changeset/techdocs-real-geese-dress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@techdocs/cli': patch ---- - -Adds ability to use encrypted S3 buckets by utilizing the SSE option in the AWS SDK diff --git a/.changeset/techdocs-sharp-knives-unite.md b/.changeset/techdocs-sharp-knives-unite.md deleted file mode 100644 index aa3625b883..0000000000 --- a/.changeset/techdocs-sharp-knives-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Use a better checkbox rendering in a task list. diff --git a/.changeset/techdocs-silver-plums-speak.md b/.changeset/techdocs-silver-plums-speak.md deleted file mode 100644 index 84d04fc1ef..0000000000 --- a/.changeset/techdocs-silver-plums-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Updates reader component used to display techdocs documentation. A previous change made this component not usable out of a page which don't have entityRef in url parameters. Reader component EntityRef parameter is now used instead of url parameters. Techdocs documentation component can now be used in our custom pages. diff --git a/.changeset/ten-planes-remember.md b/.changeset/ten-planes-remember.md deleted file mode 100644 index 3fefe650af..0000000000 --- a/.changeset/ten-planes-remember.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch ---- - -Add catalog fact retrievers - -Add fact retrievers which generate facts related to the completeness -of entity data in the catalog. diff --git a/.changeset/tender-gorillas-peel.md b/.changeset/tender-gorillas-peel.md deleted file mode 100644 index f7d7022740..0000000000 --- a/.changeset/tender-gorillas-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -bump `@rollup/plugin-commonjs` from 17.1.0 to 21.0.1 diff --git a/.changeset/tidy-beans-reflect.md b/.changeset/tidy-beans-reflect.md deleted file mode 100644 index 20796075f0..0000000000 --- a/.changeset/tidy-beans-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Introduces new `backstage-cli create` command to replace `create-plugin` and make space for creating a wider array of things. The create command also adds a new template for creating isomorphic common plugin packages. diff --git a/.changeset/twenty-swans-matter.md b/.changeset/twenty-swans-matter.md deleted file mode 100644 index 55cd3d6dc3..0000000000 --- a/.changeset/twenty-swans-matter.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -The `createApp` function from `@backstage/core-app-api` has been deprecated, with two new options being provided as a replacement. - -The first and most commonly used one is `createApp` from the new `@backstage/app-defaults` package, which behaves just like the existing `createApp`. In the future this method is likely to be expanded to add more APIs and other pieces into the default setup, for example the Utility APIs from `@backstage/integration-react`. - -The other option that we now provide is to use `createSpecializedApp` from `@backstage/core-app-api`. This is a more low-level API where you need to provide a full set of options, including your own `components`, `icons`, `defaultApis`, and `themes`. The `createSpecializedApp` way of creating an app is particularly useful if you are not using `@backstage/core-components` or MUI, as it allows you to avoid those dependencies completely. diff --git a/.changeset/unlucky-chefs-arrive.md b/.changeset/unlucky-chefs-arrive.md deleted file mode 100644 index 4b1450b281..0000000000 --- a/.changeset/unlucky-chefs-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -move the BehaviorSubject init into the constructor diff --git a/.changeset/violet-panthers-care.md b/.changeset/violet-panthers-care.md deleted file mode 100644 index 7a43c87f37..0000000000 --- a/.changeset/violet-panthers-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -adds getDefaultProcessor method to CatalogBuilder diff --git a/.changeset/yellow-deers-act.md b/.changeset/yellow-deers-act.md deleted file mode 100644 index a9008ea41e..0000000000 --- a/.changeset/yellow-deers-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Deprecated the `Error` and `ErrorContext` types, replacing them with identical `ErrorApiError` and `ErrorApiErrorContext` types. diff --git a/.changeset/yellow-falcons-whisper.md b/.changeset/yellow-falcons-whisper.md deleted file mode 100644 index 63a5d42fb0..0000000000 --- a/.changeset/yellow-falcons-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Expose template metadata to custom action handler in Scaffolder. diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md new file mode 100644 index 0000000000..b2b50ae48b --- /dev/null +++ b/packages/app-defaults/CHANGELOG.md @@ -0,0 +1,10 @@ +# @backstage/app-defaults + +## 0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 9438966b3b..7388a44ce0 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "0.1.0", + "version": "0.1.1", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-app-api": "^0.1.20", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,8 +39,8 @@ "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@types/jest": "^26.0.7", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 1dee41af56..f6f6049d68 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,50 @@ # example-app +## 0.2.53 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/plugin-cost-insights@0.11.11 + - @backstage/cli@0.9.0 + - @backstage/plugin-gcp-projects@0.3.9 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-scaffolder@0.11.11 + - @backstage/plugin-api-docs@0.6.14 + - @backstage/plugin-azure-devops@0.1.4 + - @backstage/plugin-badges@0.2.14 + - @backstage/plugin-catalog-graph@0.2.2 + - @backstage/plugin-catalog-import@0.7.4 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/plugin-circleci@0.2.29 + - @backstage/plugin-cloudbuild@0.2.28 + - @backstage/plugin-code-coverage@0.1.18 + - @backstage/plugin-explore@0.3.21 + - @backstage/plugin-github-actions@0.4.24 + - @backstage/plugin-home@0.4.6 + - @backstage/plugin-jenkins@0.5.12 + - @backstage/plugin-kafka@0.2.21 + - @backstage/plugin-kubernetes@0.4.20 + - @backstage/plugin-lighthouse@0.2.30 + - @backstage/plugin-newrelic@0.3.9 + - @backstage/plugin-pagerduty@0.3.18 + - @backstage/plugin-rollbar@0.3.19 + - @backstage/plugin-search@0.4.18 + - @backstage/plugin-sentry@0.3.29 + - @backstage/plugin-tech-radar@0.4.12 + - @backstage/plugin-techdocs@0.12.6 + - @backstage/plugin-todo@0.1.15 + - @backstage/plugin-user-settings@0.3.11 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + - @backstage/plugin-shortcuts@0.1.14 + - @backstage/app-defaults@0.1.1 + - @backstage/integration-react@0.1.14 + - @backstage/plugin-graphiql@0.2.21 + - @backstage/plugin-org@0.3.28 + ## 0.2.51 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 9a1fb108f6..a77afd13d6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,48 +1,48 @@ { "name": "example-app", - "version": "0.2.51", + "version": "0.2.53", "private": true, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^0.1.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/cli": "^0.8.0", - "@backstage/core-app-api": "^0.1.18", - "@backstage/core-components": "^0.7.1", - "@backstage/core-plugin-api": "^0.1.11", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-api-docs": "^0.6.12", - "@backstage/plugin-azure-devops": "^0.1.1", - "@backstage/plugin-badges": "^0.2.13", - "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-graph": "^0.2.1", - "@backstage/plugin-catalog-import": "^0.7.3", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/plugin-circleci": "^0.2.27", - "@backstage/plugin-cloudbuild": "^0.2.27", - "@backstage/plugin-code-coverage": "^0.1.15", - "@backstage/plugin-cost-insights": "^0.11.10", - "@backstage/plugin-explore": "^0.3.20", - "@backstage/plugin-gcp-projects": "^0.3.8", - "@backstage/plugin-github-actions": "^0.4.22", - "@backstage/plugin-graphiql": "^0.2.20", - "@backstage/plugin-home": "^0.4.4", - "@backstage/plugin-jenkins": "^0.5.11", - "@backstage/plugin-kafka": "^0.2.19", - "@backstage/plugin-kubernetes": "^0.4.17", - "@backstage/plugin-lighthouse": "^0.2.29", - "@backstage/plugin-newrelic": "^0.3.8", - "@backstage/plugin-org": "^0.3.27", - "@backstage/plugin-pagerduty": "0.3.17", - "@backstage/plugin-rollbar": "^0.3.18", - "@backstage/plugin-scaffolder": "^0.11.8", - "@backstage/plugin-search": "^0.4.15", - "@backstage/plugin-sentry": "^0.3.26", - "@backstage/plugin-shortcuts": "^0.1.12", - "@backstage/plugin-tech-radar": "^0.4.11", - "@backstage/plugin-techdocs": "^0.12.3", - "@backstage/plugin-todo": "^0.1.14", - "@backstage/plugin-user-settings": "^0.3.10", + "@backstage/app-defaults": "^0.1.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-api-docs": "^0.6.14", + "@backstage/plugin-azure-devops": "^0.1.4", + "@backstage/plugin-badges": "^0.2.14", + "@backstage/plugin-catalog": "^0.7.3", + "@backstage/plugin-catalog-graph": "^0.2.2", + "@backstage/plugin-catalog-import": "^0.7.4", + "@backstage/plugin-catalog-react": "^0.6.4", + "@backstage/plugin-circleci": "^0.2.29", + "@backstage/plugin-cloudbuild": "^0.2.28", + "@backstage/plugin-code-coverage": "^0.1.18", + "@backstage/plugin-cost-insights": "^0.11.11", + "@backstage/plugin-explore": "^0.3.21", + "@backstage/plugin-gcp-projects": "^0.3.9", + "@backstage/plugin-github-actions": "^0.4.24", + "@backstage/plugin-graphiql": "^0.2.21", + "@backstage/plugin-home": "^0.4.6", + "@backstage/plugin-jenkins": "^0.5.12", + "@backstage/plugin-kafka": "^0.2.21", + "@backstage/plugin-kubernetes": "^0.4.20", + "@backstage/plugin-lighthouse": "^0.2.30", + "@backstage/plugin-newrelic": "^0.3.9", + "@backstage/plugin-org": "^0.3.28", + "@backstage/plugin-pagerduty": "0.3.18", + "@backstage/plugin-rollbar": "^0.3.19", + "@backstage/plugin-scaffolder": "^0.11.11", + "@backstage/plugin-search": "^0.4.18", + "@backstage/plugin-sentry": "^0.3.29", + "@backstage/plugin-shortcuts": "^0.1.14", + "@backstage/plugin-tech-radar": "^0.4.12", + "@backstage/plugin-techdocs": "^0.12.6", + "@backstage/plugin-todo": "^0.1.15", + "@backstage/plugin-user-settings": "^0.3.11", "@backstage/search-common": "^0.2.0", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.12.2", @@ -64,7 +64,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/test-utils": "^0.1.19", + "@backstage/test-utils": "^0.1.22", "@rjsf/core": "^3.0.0", "@testing-library/cypress": "^7.0.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index f3464917aa..3ab36cc679 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-common +## 0.9.10 + +### Patch Changes + +- d7c1e0e34a: Added the `isDatabaseConflictError` function. +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- 1e99c73c75: Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs. +- 1daada3a06: Paths can be specified in backend.reading.allow to further restrict allowed targets +- 7ad9a07b27: Adding config prop `pluginDivisionMode` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db. +- 01f74aa878: Add `AbortSignal` support to `UrlReader` +- a8732a1200: Make sure that server builder `start()` propagates errors (such as failing to bind to the required port) properly and doesn't resolve the promise prematurely. + + After this change, the backend logger will be able to actually capture the error as it happens: + + ``` + 2021-11-11T10:54:21.334Z backstage info Initializing http server + 2021-11-11T10:54:21.335Z backstage error listen EADDRINUSE: address already in use :::7000 code=EADDRINUSE errno=-48 syscall=listen address=:: port=7000 + ``` + +- 26b5da1c1a: Do not redact empty or one-character strings. These imply that it's just a test or local dev, and unnecessarily ruin the log output. +- Updated dependencies + - @backstage/config-loader@0.8.0 + - @backstage/cli-common@0.1.6 + ## 0.9.9 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ce392a782b..9e0187137d 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.9.9", + "version": "0.9.10", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.5", + "@backstage/cli-common": "^0.1.6", "@backstage/config": "^0.1.11", - "@backstage/config-loader": "^0.7.2", + "@backstage/config-loader": "^0.8.0", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/types": "^0.1.1", @@ -80,8 +80,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 10db5cfaac..2bc6474615 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/types": "^0.1.1", @@ -43,8 +43,8 @@ "zod": "^3.9.5" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.8", - "@backstage/cli": "^0.8.1", + "@backstage/backend-test-utils": "^0.1.9", + "@backstage/cli": "^0.9.0", "jest": "^26.0.1", "wait-for-expect": "^3.0.2" }, diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 287e40a296..9927aa3cc7 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-test-utils +## 0.1.9 + +### Patch Changes + +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- Updated dependencies + - @backstage/cli@0.9.0 + - @backstage/backend-common@0.9.10 + ## 0.1.8 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 299b5588a2..c3d4f730b7 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.8", + "version": "0.1.9", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/cli": "^0.8.0", + "@backstage/backend-common": "^0.9.10", + "@backstage/cli": "^0.9.0", "@backstage/config": "^0.1.9", "knex": "^0.95.1", "mysql2": "^2.2.5", @@ -41,7 +41,7 @@ "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "jest": "^26.0.1" }, "files": [ diff --git a/packages/backend/package.json b/packages/backend/package.json index 7fdffc087f..4fb692f040 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -24,31 +24,31 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/integration": "^0.6.8", - "@backstage/plugin-app-backend": "^0.3.17", - "@backstage/plugin-auth-backend": "^0.4.7", - "@backstage/plugin-azure-devops-backend": "^0.2.0", + "@backstage/plugin-app-backend": "^0.3.19", + "@backstage/plugin-auth-backend": "^0.4.8", + "@backstage/plugin-azure-devops-backend": "^0.2.1", "@backstage/plugin-badges-backend": "^0.1.11", - "@backstage/plugin-catalog-backend": "^0.17.3", + "@backstage/plugin-catalog-backend": "^0.17.4", "@backstage/plugin-code-coverage-backend": "^0.1.14", "@backstage/plugin-graphql-backend": "^0.1.9", "@backstage/plugin-jenkins-backend": "^0.1.7", "@backstage/plugin-kubernetes-backend": "^0.3.18", - "@backstage/plugin-kafka-backend": "^0.2.10", + "@backstage/plugin-kafka-backend": "^0.2.11", "@backstage/plugin-proxy-backend": "^0.2.13", "@backstage/plugin-rollbar-backend": "^0.1.15", - "@backstage/plugin-scaffolder-backend": "^0.15.12", + "@backstage/plugin-scaffolder-backend": "^0.15.13", "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.7", "@backstage/plugin-search-backend": "^0.2.6", "@backstage/plugin-search-backend-node": "^0.4.2", "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.5", "@backstage/plugin-search-backend-module-pg": "^0.2.1", - "@backstage/plugin-techdocs-backend": "^0.10.5", - "@backstage/plugin-tech-insights-backend": "^0.1.0", + "@backstage/plugin-techdocs-backend": "^0.10.8", + "@backstage/plugin-tech-insights-backend": "^0.1.1", "@backstage/plugin-tech-insights-node": "^0.1.0", "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0", "@backstage/plugin-todo-backend": "^0.1.13", @@ -56,7 +56,7 @@ "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^11.0.1", "dockerode": "^3.3.1", - "example-app": "^0.2.51", + "example-app": "^0.2.53", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-prom-bundle": "^6.3.6", @@ -68,7 +68,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index f5ec2116f7..1b11e3bcb2 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-client +## 0.5.2 + +### Patch Changes + +- 3bf2238187: Update to the right version of @backstage/errors +- Updated dependencies + - @backstage/catalog-model@0.9.7 + ## 0.5.1 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 9caac50c90..4ab727b6b3 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "0.5.1", + "version": "0.5.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", + "@backstage/catalog-model": "^0.9.7", "@backstage/errors": "^0.1.4", "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "msw": "^0.35.0" }, diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index e0c067c9c7..efd6513cdd 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/catalog-model +## 0.9.7 + +### Patch Changes + +- 8809b6c0dd: Update the json-schema dependency version. + ## 0.9.6 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 17c89ed824..0ba07aec3c 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "0.9.6", + "version": "0.9.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -42,7 +42,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", "yaml": "^1.9.2" diff --git a/packages/cli-common/CHANGELOG.md b/packages/cli-common/CHANGELOG.md index f3978798c6..e588c02edf 100644 --- a/packages/cli-common/CHANGELOG.md +++ b/packages/cli-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/cli-common +## 0.1.6 + +### Patch Changes + +- 677bfc2dd0: Keep backstage.json in sync + + The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present. + ## 0.1.5 ### Patch Changes diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 5916cdb017..cdb4b01b6d 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.5", + "version": "0.1.6", "private": false, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 0fc702851d..407c4fcbf2 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/cli +## 0.9.0 + +### Minor Changes + +- 25f637f39f: Tweaked style insertion logic to make sure that JSS stylesheets always receive the highest priority. + +### Patch Changes + +- 677bfc2dd0: Keep backstage.json in sync + + The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present. + +- 8809b6c0dd: Update the json-schema dependency version. +- fdfd2f8a62: remove double config dep +- 1e99c73c75: Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs. +- 6dcfe227a2: Added a scaffolder backend module template for the `create` command. +- 4ca3542fdd: Fixed a bug where calling `backstage-cli backend:bundle --build-dependencies` with no dependencies to be built would cause all monorepo packages to be built instead. +- 867ea81d15: bump `@rollup/plugin-commonjs` from 17.1.0 to 21.0.1 +- 16d06f6ac3: Introduces new `backstage-cli create` command to replace `create-plugin` and make space for creating a wider array of things. The create command also adds a new template for creating isomorphic common plugin packages. +- Updated dependencies + - @backstage/config-loader@0.8.0 + - @backstage/cli-common@0.1.6 + ## 0.8.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index fcbc00f47d..1eed5a742c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.8.2", + "version": "0.9.0", "private": false, "publishConfig": { "access": "public" @@ -28,9 +28,9 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.5", + "@backstage/cli-common": "^0.1.6", "@backstage/config": "^0.1.11", - "@backstage/config-loader": "^0.7.2", + "@backstage/config-loader": "^0.8.0", "@backstage/errors": "^0.1.4", "@backstage/types": "^0.1.1", "@hot-loader/react-dom": "^16.13.0", @@ -117,13 +117,13 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.9", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@backstage/theme": "^0.2.13", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index 1043d9718f..bf0711ad6f 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/codemods +## 0.1.22 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.6 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + ## 0.1.21 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 17e89c86f0..bfb8d4d4b3 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.21", + "version": "0.1.22", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "backstage-codemods": "bin/backstage-codemods" }, "dependencies": { - "@backstage/cli-common": "0.1.5", + "@backstage/cli-common": "0.1.6", "@backstage/core-app-api": "*", "@backstage/core-components": "*", "@backstage/core-plugin-api": "*", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 0f1bc27996..00ace6e704 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/config-loader +## 0.8.0 + +### Minor Changes + +- 1e99c73c75: Update `loadConfig` to return `LoadConfigResult` instead of an array of `AppConfig`. + + This function is primarily used internally by other config loaders like `loadBackendConfig` which means no changes are required for most users. + + If you use `loadConfig` directly you will need to update your usage from: + + ```diff + - const appConfigs = await loadConfig(options) + + const { appConfigs } = await loadConfig(options) + ``` + +### Patch Changes + +- 8809b6c0dd: Update the json-schema dependency version. +- Updated dependencies + - @backstage/cli-common@0.1.6 + ## 0.7.2 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 76dcec35c6..b4c0350def 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.7.2", + "version": "0.8.0", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.5", + "@backstage/cli-common": "^0.1.6", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/types": "^0.1.1", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index b9e60b7dab..342d453eae 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/core-app-api +## 0.1.21 + +### Patch Changes + +- 0b1de52732: Migrated to using new `ErrorApiError` and `ErrorApiErrorContext` names. +- ecd1fcb80a: Deprecated the `BackstagePluginWithAnyOutput` type. +- 32bfbafb0f: Start exporting and marking several types as public to address errors in the API report. +- 014cbf8cb9: The `createApp` function from `@backstage/core-app-api` has been deprecated, with two new options being provided as a replacement. + + The first and most commonly used one is `createApp` from the new `@backstage/app-defaults` package, which behaves just like the existing `createApp`. In the future this method is likely to be expanded to add more APIs and other pieces into the default setup, for example the Utility APIs from `@backstage/integration-react`. + + The other option that we now provide is to use `createSpecializedApp` from `@backstage/core-app-api`. This is a more low-level API where you need to provide a full set of options, including your own `components`, `icons`, `defaultApis`, and `themes`. The `createSpecializedApp` way of creating an app is particularly useful if you are not using `@backstage/core-components` or MUI, as it allows you to avoid those dependencies completely. + +- 475edb5bc5: move the BehaviorSubject init into the constructor +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/app-defaults@0.1.1 + ## 0.1.20 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index fc6c103109..4c79f9eb3f 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.20", + "version": "0.1.21", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/app-defaults": "^0.1.0", - "@backstage/core-components": "^0.7.3", + "@backstage/app-defaults": "^0.1.1", + "@backstage/core-components": "^0.7.4", "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.0", @@ -47,8 +47,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 1e62df337d..c62ba9e90d 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-components +## 0.7.4 + +### Patch Changes + +- 274a4fc633: Add Props Icon for Sidebar Item SidebarSearchField and Settings +- 682945e233: Changing the `Header` styles to use more theme variables. With this the title `font-size` will not change on resizing the window. +- 892c1d9202: Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken +- Updated dependencies + - @backstage/core-plugin-api@0.2.0 + ## 0.7.3 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index ffbde0072a..afa9af0341 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.7.3", + "version": "0.7.4", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", "@backstage/theme": "^0.2.13", "@material-table/core": "^3.1.0", @@ -67,9 +67,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.20", - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/core-app-api": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index d92922ddb4..a8d35e5a14 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/core-plugin-api +## 0.2.0 + +### Minor Changes + +- 7e18ed7f29: Removed the unused `UserFlags` type. +- 7df99cdb77: Remove exports of unused types(`RouteOptions` and `RoutePath`). + +### Patch Changes + +- 37ebea2d68: Add deprecation warnings around `title` `icon` and `path` as they are no longer controlled when creating `routeRefs` +- 2dd2a7b2cc: Deprecated the `theme` property on `AppTheme`, replacing it with `Provider`. See https://backstage.io/docs/api/deprecations#app-theme for more details. +- b6a4bacdc4: Deprecated the `Error` and `ErrorContext` types, replacing them with identical `ErrorApiError` and `ErrorApiErrorContext` types. + ## 0.1.13 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 6679e89645..e5b4572dff 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "0.1.13", + "version": "0.2.0", "private": false, "publishConfig": { "access": "public", @@ -43,9 +43,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 7e53d0cde5..af1fd58ead 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/create-app +## 0.4.4 + +### Patch Changes + +- 4ebc9fd277: Create backstage.json file + + `@backstage/create-app` will create a new `backstage.json` file. At this point, the file will contain a `version` property, representing the version of `@backstage/create-app` used for creating the application. If the backstage's application has been bootstrapped using an older version of `@backstage/create-app`, the `backstage.json` file can be created and kept in sync, together with all the changes of the latest version of backstage, by running the following script: + + ```bash + yarn backstage-cli versions:bump + ``` + +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- 014cbf8cb9: Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future. + + To migrate an existing application, add the latest version of `@backstage/app-defaults` as a dependency in `packages/app/package.json`, and make the following change to `packages/app/src/App.tsx`: + + ```diff + -import { createApp, FlatRoutes } from '@backstage/core-app-api'; + +import { createApp } from '@backstage/app-defaults'; + +import { FlatRoutes } from '@backstage/core-app-api'; + ``` + +- 2163e83fa2: Refactor and add regression tests for create-app tasks +- Updated dependencies + - @backstage/cli-common@0.1.6 + ## 0.4.3 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 7c1feec683..d5ee616ff5 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.3", + "version": "0.4.4", "private": false, "publishConfig": { "access": "public" @@ -28,7 +28,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.5", + "@backstage/cli-common": "^0.1.6", "chalk": "^4.0.0", "commander": "^6.1.0", "fs-extra": "9.1.0", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 6afb45d79b..b882155a3d 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/dev-utils +## 0.2.13 + +### Patch Changes + +- 58a4e67ded: Add theme switcher to sidebar of dev app. +- 014cbf8cb9: Migrated to using `@backstage/app-defaults`. +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + - @backstage/test-utils@0.1.22 + - @backstage/app-defaults@0.1.1 + - @backstage/integration-react@0.1.14 + ## 0.2.12 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 974329e8d4..32d287d35d 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.2.12", + "version": "0.2.13", "private": false, "publishConfig": { "access": "public", @@ -29,14 +29,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/app-defaults": "^0.1.0", - "@backstage/core-app-api": "^0.1.18", - "@backstage/core-components": "^0.7.1", - "@backstage/core-plugin-api": "^0.1.11", - "@backstage/catalog-model": "^0.9.5", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/test-utils": "^0.1.19", + "@backstage/app-defaults": "^0.1.1", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/catalog-model": "^0.9.7", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.6.4", + "@backstage/test-utils": "^0.1.22", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -53,7 +53,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/embedded-techdocs-app/CHANGELOG.md b/packages/embedded-techdocs-app/CHANGELOG.md new file mode 100644 index 0000000000..6a9583c2dc --- /dev/null +++ b/packages/embedded-techdocs-app/CHANGELOG.md @@ -0,0 +1,17 @@ +# embedded-techdocs-app + +## 0.2.53 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/cli@0.9.0 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-techdocs@0.12.6 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + - @backstage/test-utils@0.1.22 + - @backstage/app-defaults@0.1.1 + - @backstage/integration-react@0.1.14 diff --git a/packages/embedded-techdocs-app/package.json b/packages/embedded-techdocs-app/package.json index 412fd80357..b2d7882f8f 100644 --- a/packages/embedded-techdocs-app/package.json +++ b/packages/embedded-techdocs-app/package.json @@ -1,20 +1,20 @@ { "name": "embedded-techdocs-app", - "version": "0.0.0", + "version": "0.2.53", "private": true, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^0.1.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/cli": "^0.8.0", + "@backstage/app-defaults": "^0.1.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/cli": "^0.9.0", "@backstage/config": "^0.1.10", - "@backstage/core-app-api": "^0.1.18", - "@backstage/core-components": "^0.7.1", - "@backstage/core-plugin-api": "^0.1.11", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-techdocs": "^0.12.3", - "@backstage/test-utils": "^0.1.19", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog": "^0.7.3", + "@backstage/plugin-techdocs": "^0.12.6", + "@backstage/test-utils": "^0.1.22", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -26,7 +26,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/packages/errors/package.json b/packages/errors/package.json index fe62bb3bba..38fe6c634e 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -35,7 +35,7 @@ "serialize-error": "^8.0.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7" }, "files": [ diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index a2a0d0c368..e972b4d4e8 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/integration-react +## 0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.13 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index b703203ab2..f3c79cc56c 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "0.1.13", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.2", - "@backstage/core-plugin-api": "^0.1.12", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/integration": "^0.6.9", "@backstage/theme": "^0.2.12", "@material-ui/core": "^4.12.2", @@ -34,9 +34,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.9.0", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/packages/integration/package.json b/packages/integration/package.json index bd49ecc419..4ccde52a70 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -39,9 +39,9 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/config-loader": "^0.7.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.9.0", + "@backstage/config-loader": "^0.8.0", + "@backstage/test-utils": "^0.1.22", "@types/jest": "^26.0.7", "@types/luxon": "^2.0.4", "msw": "^0.35.0" diff --git a/packages/search-common/package.json b/packages/search-common/package.json index e913ef66ff..d65e9bcbdc 100644 --- a/packages/search-common/package.json +++ b/packages/search-common/package.json @@ -39,7 +39,7 @@ "@backstage/types": "^0.1.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1" + "@backstage/cli": "^0.9.0" }, "jest": { "roots": [ diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 0f7c6be52f..6d69e452ad 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,17 @@ # @techdocs/cli +## 0.8.6 + +### Patch Changes + +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- 1578ad341b: Add support for specifying bucketRootPath for AWS and GCS publishers +- f2694e3750: Adds ability to use encrypted S3 buckets by utilizing the SSE option in the AWS SDK +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + - @backstage/techdocs-common@0.10.7 + ## 0.8.5 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 28903ccb9a..70ca0a9668 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "0.8.5", + "version": "0.8.6", "private": false, "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ "techdocs-cli": "bin/techdocs-cli" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/commander": "^2.12.2", "@types/fs-extra": "^9.0.6", "@types/http-proxy": "^1.17.4", @@ -41,7 +41,7 @@ "@types/react-dev-utils": "^9.0.4", "@types/serve-handler": "^6.1.0", "@types/webpack-env": "^1.15.3", - "embedded-techdocs-app": "0.0.0", + "embedded-techdocs-app": "0.2.53", "find-process": "^1.4.5", "nodemon": "^2.0.2", "ts-node": "^10.0.0" @@ -56,10 +56,10 @@ "ext": "ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/techdocs-common": "^0.10.4", + "@backstage/techdocs-common": "^0.10.7", "@types/dockerode": "^3.3.0", "commander": "^6.1.0", "dockerode": "^3.3.1", diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 87e9e12193..69bcbcdaeb 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/techdocs-common +## 0.10.7 + +### Patch Changes + +- 0b60a051c9: Added OpenStack Swift case migration support. +- 9e64a7ac1e: Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + ## 0.10.6 ### Patch Changes diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index b44e68374d..b13b94a1a7 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.10.6", + "version": "0.10.7", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,8 +38,8 @@ "dependencies": { "@azure/identity": "^1.5.0", "@azure/storage-blob": "^12.5.0", - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-model": "^0.9.6", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/search-common": "^0.2.1", @@ -60,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/fs-extra": "^9.0.5", "@types/js-yaml": "^4.0.0", "@types/mime-types": "^2.1.0", diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 6fcacc30fa..3460e6c714 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/test-utils +## 0.1.22 + +### Patch Changes + +- 0b1de52732: Migrated to using new `ErrorApiError` and `ErrorApiErrorContext` names. +- 2dd2a7b2cc: Migrated to using `createSpecializedApp`. +- Updated dependencies + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + ## 0.1.21 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 6f5f78de8c..79667c236a 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.21", + "version": "0.1.22", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.20", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -46,7 +46,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/theme/package.json b/packages/theme/package.json index d9e89c969f..28a05af98d 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -31,7 +31,7 @@ "@material-ui/core": "^4.12.2" }, "devDependencies": { - "@backstage/cli": "^0.8.2" + "@backstage/cli": "^0.9.0" }, "files": [ "dist" diff --git a/packages/types/package.json b/packages/types/package.json index 8632422a25..197d51b061 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -31,7 +31,7 @@ }, "dependencies": {}, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/zen-observable": "^0.8.0", "zen-observable": "^0.8.15" }, diff --git a/packages/version-bridge/package.json b/packages/version-bridge/package.json index 7cc0b56a34..35a6e8f71d 100644 --- a/packages/version-bridge/package.json +++ b/packages/version-bridge/package.json @@ -33,7 +33,7 @@ "react": "^16.12.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2" diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index 3de49b9b74..f769b6e133 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-allure +## 0.1.7 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index b157795eec..6a9a55bceb 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -36,10 +36,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index a59bcb6351..acc5c235f9 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-analytics-module-ga +## 0.1.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.2 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 4970f50c04..b83aed37c0 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -34,10 +34,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 78f4b08bd7..0b0bd4890c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-api-docs +## 0.6.14 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.6.13 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 61ae26c776..16f2d0d188 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.6.13", + "version": "0.6.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ }, "dependencies": { "@asyncapi/react-component": "^0.23.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -53,10 +53,10 @@ "swagger-ui-react": "^4.0.0-rc.3" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 0658b10ebb..a58b028049 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-backend +## 0.3.19 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@0.8.0 + - @backstage/backend-common@0.9.10 + ## 0.3.18 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index d989457c79..ebb0393192 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.18", + "version": "0.3.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/config-loader": "^0.7.1", + "@backstage/backend-common": "^0.9.10", + "@backstage/config-loader": "^0.8.0", "@backstage/config": "^0.1.11", "@backstage/types": "^0.1.1", "@types/express": "^4.17.6", @@ -42,7 +42,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.9.0", "@backstage/types": "^0.1.1", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index a11f561986..5dc0cdb861 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend +## 0.4.8 + +### Patch Changes + +- 892c1d9202: Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + - @backstage/test-utils@0.1.22 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index e9ec29970b..3a9e8102eb 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.4.7", + "version": "0.4.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.6", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", - "@backstage/test-utils": "^0.1.21", + "@backstage/test-utils": "^0.1.22", "@google-cloud/firestore": "^4.15.1", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", @@ -73,7 +73,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index d2fe981eb0..06fc4833c4 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-azure-devops-backend +## 0.2.1 + +### Patch Changes + +- 2b5ccd2964: Improved Date handling for the Azure DevOps set of plugins by using strings and letting the frontend handle the conversion to DateTime +- Updated dependencies + - @backstage/backend-common@0.9.10 + - @backstage/plugin-azure-devops-common@0.1.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 901ad0b960..6d213cbce6 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.11", - "@backstage/plugin-azure-devops-common": "^0.0.2", + "@backstage/plugin-azure-devops-common": "^0.1.0", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "msw": "^0.35.0" diff --git a/plugins/azure-devops-common/CHANGELOG.md b/plugins/azure-devops-common/CHANGELOG.md index b414cd3ce6..4a834fb068 100644 --- a/plugins/azure-devops-common/CHANGELOG.md +++ b/plugins/azure-devops-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-azure-devops-common +## 0.1.0 + +### Minor Changes + +- 2b5ccd2964: Improved Date handling for the Azure DevOps set of plugins by using strings and letting the frontend handle the conversion to DateTime + ## 0.0.2 ### Patch Changes diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index a1ddddcbe0..fe9df644b1 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-common", - "version": "0.0.2", + "version": "0.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.8.2" + "@backstage/cli": "^0.9.0" }, "files": [ "dist" diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 2f75f64236..0bcd9bc686 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-azure-devops +## 0.1.4 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- b5eac957f2: Added entity view for Azure Repo Pull Requests +- 2b5ccd2964: Improved Date handling for the Azure DevOps set of plugins by using strings and letting the frontend handle the conversion to DateTime +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/plugin-azure-devops-common@0.1.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 3f1876b974..0642a24cac 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,12 +27,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", - "@backstage/plugin-azure-devops-common": "^0.0.2", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-azure-devops-common": "^0.1.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,10 +45,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 51a181612a..2186c8d52e 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.3", "@types/express": "^4.17.6", @@ -46,7 +46,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 3a6ed61717..44bc597d0a 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-badges +## 0.2.14 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.13 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 0c792808bc..dcc422ec19 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.13", + "version": "0.2.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,11 +27,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md new file mode 100644 index 0000000000..88e646b5e3 --- /dev/null +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -0,0 +1,10 @@ +# @backstage/plugin-bazaar-backend + +## 0.1.2 + +### Patch Changes + +- f6ba309d9e: A Bazaar project has been extended with the following fields: size, start date (optional), end date (optional) and a responsible person. +- Updated dependencies + - @backstage/backend-common@0.9.10 + - @backstage/backend-test-utils@0.1.9 diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index a9ac0153b8..a6610493e2 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/backend-test-utils": "^0.1.8", + "@backstage/backend-common": "^0.9.10", + "@backstage/backend-test-utils": "^0.1.9", "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0" + "@backstage/cli": "^0.9.0" }, "files": [ "dist", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 731f62b9e9..8988bf5d62 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-bazaar +## 0.1.4 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- f6ba309d9e: A Bazaar project has been extended with the following fields: size, start date (optional), end date (optional) and a responsible person. +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/cli@0.9.0 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 094741bf36..fd07a3b5c7 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/cli": "^0.8.2", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/catalog-model": "^0.9.7", + "@backstage/cli": "^0.9.0", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -41,8 +41,8 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/dev-utils": "^0.2.12", + "@backstage/cli": "^0.9.0", + "@backstage/dev-utils": "^0.2.13", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.0.6" }, diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 7e364ca294..874cbe2c6c 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-bitrise +## 0.1.17 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 5ebfd453a2..28a3c88ad1 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.16", + "version": "0.1.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,10 +39,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index a8c3b71345..5e3cec24d9 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-backend": "^0.17.2", + "@backstage/plugin-catalog-backend": "^0.17.4", "@backstage/types": "^0.1.1", "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", @@ -40,7 +40,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1", + "@backstage/cli": "^0.9.0", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index b3c9494f78..222f56b022 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -30,9 +30,9 @@ }, "dependencies": { "@azure/msal-node": "^1.1.0", - "@backstage/catalog-model": "^0.9.5", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/plugin-catalog-backend": "^0.17.3", + "@backstage/plugin-catalog-backend": "^0.17.4", "@microsoft/microsoft-graph-types": "^2.6.0", "cross-fetch": "^3.0.6", "lodash": "^4.17.21", @@ -41,9 +41,9 @@ "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/backend-common": "^0.9.10", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@types/lodash": "^4.14.151", "msw": "^0.35.0" }, diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index abb50a345c..92b57f3483 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend +## 0.17.4 + +### Patch Changes + +- 5d2a7303bd: This fixes a bug where locations couldn't be added unless the processing engine is started. + It's now possible to run the catalog backend without starting the processing engine and still allowing locations registrations. + + This is done by refactor the `EntityProvider.connect` to happen outside the engine. + +- 06934f2f52: Adjust entity query construction to ensure sub-queries are always isolated from one another. +- b90fc74d70: adds getDefaultProcessor method to CatalogBuilder +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + ## 0.17.3 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index f13ff2d66c..a9a35d5858 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "0.17.3", + "version": "0.17.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.6", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", @@ -62,9 +62,9 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.8", - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/backend-test-utils": "^0.1.9", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index a156be8fe0..c107a4e758 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-graph +## 0.2.2 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index e58d66b1f2..53fde11c7b 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index 5df4f9ba5c..a7070a1634 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/types": "^0.1.1", "@graphql-modules/core": "^0.7.17", @@ -43,8 +43,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@graphql-codegen/cli": "^1.21.3", "@graphql-codegen/typescript": "^1.17.7", "@graphql-codegen/typescript-resolvers": "^1.17.7", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index d06f38698b..6397718fd7 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-import +## 0.7.4 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + ## 0.7.3 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 242231bf81..bd7f99c884 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.7.3", + "version": "0.7.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,14 +31,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.6.4", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -55,10 +55,10 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 79dfa5e72a..e6d00b2f8f 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-react +## 0.6.4 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/core-app-api@0.1.21 + ## 0.6.3 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 9778163391..2ae853650a 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "0.6.3", + "version": "0.6.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.6", - "@backstage/core-app-api": "^0.1.20", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-app-api": "^0.1.21", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/types": "^0.1.1", @@ -51,8 +51,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 0030f702ec..7fda738d32 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog +## 0.7.3 + +### Patch Changes + +- 38d6df6bb9: Remove the "View Api" icon in the AboutCard, as the information is misleading for some users and is + duplicated in the tabs above. +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + ## 0.7.2 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 9697741dde..254718bd3c 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog", "description": "The Backstage plugin for browsing the Backstage catalog", - "version": "0.7.2", + "version": "0.7.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,13 +31,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 78cd6d9711..18e227627b 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-circleci +## 0.2.29 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.28 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index c57d19ea00..d7cd3b0fd2 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.2.28", + "version": "0.2.29", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index dfd17e9908..735f5d2167 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-cloudbuild +## 0.2.28 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.27 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 6521816f73..32b860ec23 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cloudbuild", "description": "A Backstage plugin that integrates towards Google Cloud Build", - "version": "0.2.27", + "version": "0.2.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 53d52a9246..90320d0e41 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/express-xml-bodyparser": "^0.3.2", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 572e0f7800..b5b413b9d2 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-code-coverage +## 0.1.18 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.17 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 0d6e998b69..0c32718e97 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.1.17", + "version": "0.1.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,10 +42,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index c8372532f6..ca889ebdbd 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-config-schema +## 0.1.13 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 0708115270..0385a64ec8 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.12", + "version": "0.1.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", @@ -37,10 +37,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 8a7b07af3b..7096af37c6 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-cost-insights +## 0.11.11 + +### Patch Changes + +- 4576f82858: Fixed generation of sample data in the example Cost Insights client +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.11.10 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 064b24bd0c..04e4f41701 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.11.10", + "version": "0.11.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -55,10 +55,10 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index 4af81f6d8b..0560527f80 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-explore-react +## 0.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@0.2.0 + ## 0.0.6 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index 1dfd24dff8..a67f8b6892 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore-react", "description": "A frontend library for Backstage plugins that want to interact with the explore plugin", - "version": "0.0.6", + "version": "0.0.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/core-plugin-api": "^0.1.11" + "@backstage/core-plugin-api": "^0.2.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", - "@backstage/dev-utils": "^0.2.0", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.9.0", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index f051691dc5..73117d7f32 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore +## 0.3.21 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/plugin-explore-react@0.0.7 + ## 0.3.20 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 30890c7176..5732e3fca6 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.3.20", + "version": "0.3.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", - "@backstage/plugin-explore-react": "^0.0.6", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", + "@backstage/plugin-explore-react": "^0.0.7", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index 2343eb1c2e..b137ed1f73 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-firehydrant +## 0.1.8 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.7 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 4e3fbdd3c8..dce261617d 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.1.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index f422dfbe41..af99e13c4a 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-fossa +## 0.2.22 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.21 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 94528a3126..8090fdc1c7 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-fossa", "description": "A Backstage plugin that integrates towards FOSSA", - "version": "0.2.21", + "version": "0.2.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,11 +32,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index 28c40f6221..ac8dd570a6 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-gcp-projects +## 0.3.9 + +### Patch Changes + +- 741bcb168e: UI updates to GCP-projects plugin + + Adds the following to the project list page: + + - pagination + - filtering + - sorting + - rows per page + - show/hide columns + + Makes breadcrumb a link back to project list for the project details and new project views. + + In project list page, updates New project button to use RouterLink instead of `href` to avoid login prompt. + + In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 471b9d80f1..0e33130274 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gcp-projects", "description": "A Backstage plugin that helps you manage projects in GCP", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 72bcf87b2a..1c627ab086 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-git-release-manager +## 0.3.3 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.2 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index b1b2c11732..0c52381514 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-git-release-manager", "description": "A Backstage plugin that helps you manage releases in git", - "version": "0.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/integration": "^0.6.8", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -39,10 +39,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index c8fce8e501..5eed309d68 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-github-actions +## 0.4.24 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.4.23 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 1a4079a5ca..789385308e 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.4.23", + "version": "0.4.24", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/integration": "^0.6.8", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index 5259ea11b2..701b167135 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-github-deployments +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + ## 0.1.20 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 78061951f5..a52bbfa95a 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-deployments", "description": "A Backstage plugin that integrates towards GitHub Deployments", - "version": "0.1.20", + "version": "0.1.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,13 +21,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", - "@backstage/integration-react": "^0.1.12", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index 08f256cdb5..42f3533d31 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-gitops-profiles +## 0.3.9 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index c290599c2c..226a45332c 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gitops-profiles", "description": "A Backstage plugin that helps you manage GitOps profiles", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index aa58dbd332..f6c8b9fde8 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-graphiql +## 0.2.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.20 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index df1ad6f887..aeefce3340 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.20", + "version": "0.2.21", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphql-backend/package.json b/plugins/graphql-backend/package.json index 9d90dd8782..335545021e 100644 --- a/plugins/graphql-backend/package.json +++ b/plugins/graphql-backend/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.8", "@backstage/plugin-catalog-graphql": "^0.2.12", "@graphql-modules/core": "^0.7.17", @@ -47,7 +47,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.35.0", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 06beb827a6..09e379a625 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-home +## 0.4.6 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.4.5 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 8020d7af03..74336d80be 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.4.5", + "version": "0.4.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index f43f2add09..1bff4b22ef 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-ilert +## 0.1.17 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 69c4025181..e0ccd3cd2d 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.1.16", + "version": "0.1.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index 7049fc0a32..ac9efb3ffd 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -22,9 +22,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/jenkins": "^0.23.1", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 84db4728bc..369676b195 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-jenkins +## 0.5.12 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.5.11 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 80d8b40322..e8115c3800 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.5.11", + "version": "0.5.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 88111cd1de..482c1f75b1 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kafka-backend +## 0.2.11 + +### Patch Changes + +- 9145449220: Update Kafka configuration types +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + ## 0.2.10 ### Patch Changes diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index e271decc8b..dea803890f 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.3", "@types/express": "^4.17.6", @@ -44,7 +44,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/jest-when": "^2.7.2", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 2fe42565da..33ec4bd95a 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kafka +## 0.2.21 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.20 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 888c79db59..bea4aadcba 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.2.20", + "version": "0.2.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index be2903dff5..4edc7b238a 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.3", "@backstage/plugin-kubernetes-common": "^0.1.4", @@ -55,7 +55,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/aws4": "^1.5.1", "supertest": "^6.1.3", "aws-sdk-mock": "^5.2.1", diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 28dfc94c49..3c9cb5e357 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -35,11 +35,11 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", + "@backstage/catalog-model": "^0.9.7", "@kubernetes/client-node": "^0.15.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0" + "@backstage/cli": "^0.9.0" }, "jest": { "roots": [ diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index a757041b32..0ae3a6c012 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes +## 0.4.20 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.4.19 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 9dbca8aff8..8f4e3356c5 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.4.19", + "version": "0.4.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/plugin-kubernetes-common": "^0.1.5", "@backstage/theme": "^0.2.13", "@kubernetes/client-node": "^0.15.0", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 4ff76d4f1b..48ef9a178d 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-lighthouse +## 0.2.30 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.29 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index f0dca639fc..b1e0290b4c 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.2.29", + "version": "0.2.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,11 +32,11 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index 7ffd4b06d1..89b77e9866 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-newrelic +## 0.3.9 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 271daa566e..c19bc9ed37 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-newrelic", "description": "A Backstage plugin that integrates towards New Relic", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 6e1cc7064f..f46095f46a 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-org +## 0.3.28 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.27 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 5348f67523..035ae50c3b 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.3.27", + "version": "0.3.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -37,10 +37,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 11ae548db8..bb74598f2a 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-pagerduty +## 0.3.18 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.17 ### Patch Changes diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 7b42a15bc6..735b541451 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", "description": "A Backstage plugin that integrates towards PagerDuty", - "version": "0.3.17", + "version": "0.3.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index 5e79c1ba7b..95a0d9d3c3 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -44,7 +44,7 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "msw": "^0.35.0" } diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index c43ea8b31a..05499d88e8 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.8", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -43,7 +43,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 1d7be09b85..be8b6e65e5 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.10", "@types/express": "^4.17.6", "axios": "^0.21.1", @@ -48,7 +48,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 4f7f3c9a16..aee0a74525 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-rollbar +## 0.3.19 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.18 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 2e2da0a284..f3d32eed11 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.3.18", + "version": "0.3.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 36a6310aa2..90258d3c28 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", + "@backstage/backend-common": "^0.9.10", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/plugin-scaffolder-backend": "^0.15.12", + "@backstage/plugin-scaffolder-backend": "^0.15.13", "@backstage/config": "^0.1.11", "@backstage/types": "^0.1.1", "command-exists": "^1.2.9", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", "@types/jest": "^26.0.7", diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index f4ace02eca..37b077568e 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/plugin-scaffolder-backend": "^0.15.12", + "@backstage/backend-common": "^0.9.10", + "@backstage/plugin-scaffolder-backend": "^0.15.13", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", @@ -31,7 +31,7 @@ "fs-extra": "^9.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", + "@backstage/cli": "^0.9.0", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/command-exists": "^1.2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index de033eabbd..f71849a1d2 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend +## 0.15.13 + +### Patch Changes + +- 26eb174ce8: Skip empty file names when scaffolding with nunjucks +- ecdcbd08ee: Expose template metadata to custom action handler in Scaffolder. +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + - @backstage/plugin-catalog-backend@0.17.4 + ## 0.15.12 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index e5d13801ba..374cfeffd6 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "0.15.12", + "version": "0.15.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,13 +30,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.6", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/plugin-catalog-backend": "^0.17.3", + "@backstage/plugin-catalog-backend": "^0.17.4", "@backstage/plugin-scaffolder-common": "^0.1.1", "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.4", "@backstage/types": "^0.1.1", @@ -71,8 +71,8 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 006d8ad7aa..05d5ae3031 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -36,10 +36,10 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/types": "^0.1.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1" + "@backstage/cli": "^0.9.0" } } diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index dd5653240a..8c43d8d010 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder +## 0.11.11 + +### Patch Changes + +- 8809b6c0dd: Update the json-schema dependency version. +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + ## 0.11.10 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 594a8e6423..3d7bde77c9 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "0.11.10", + "version": "0.11.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,15 +31,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.5.1", - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/integration-react": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -66,10 +66,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index df0a783396..5ebc7850a9 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -30,8 +30,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.9.9", - "@backstage/cli": "^0.8.2", + "@backstage/backend-common": "^0.9.10", + "@backstage/cli": "^0.9.0", "@elastic/elasticsearch-mock": "^0.3.0" }, "files": [ diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index a31d908ac4..74e9698ea4 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -20,15 +20,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", + "@backstage/backend-common": "^0.9.10", "@backstage/search-common": "^0.2.0", "@backstage/plugin-search-backend-node": "^0.4.2", "lodash": "^4.17.21", "knex": "^0.95.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.8", - "@backstage/cli": "^0.8.0" + "@backstage/backend-test-utils": "^0.1.9", + "@backstage/cli": "^0.9.0" }, "files": [ "dist", diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index 4514d07756..369c52a4f6 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -26,8 +26,8 @@ "@types/lunr": "^2.3.3" }, "devDependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/cli": "^0.8.1" + "@backstage/backend-common": "^0.9.10", + "@backstage/cli": "^0.9.0" }, "files": [ "dist" diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 18d4484451..2df51e1bb4 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", + "@backstage/backend-common": "^0.9.10", "@backstage/search-common": "^0.2.0", "@backstage/plugin-search-backend-node": "^0.4.2", "@types/express": "^4.17.6", @@ -30,7 +30,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 808c847914..ef3736b1df 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search +## 0.4.18 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- 704b267e1c: Smaller UX improvements to search components such as optional autoFocus prop to SearchBar components, decreased debounce value and closing modal on link click of SearchModal, terminology updates of SearchResultPager. +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.4.17 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 9d25ec723d..24984d10e7 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "0.4.17", + "version": "0.4.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/search-common": "^0.2.1", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index b0bcaad261..cad08289ed 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-sentry +## 0.3.29 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.28 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index ee2421bf7f..fd7cc34b1f 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.3.28", + "version": "0.3.29", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.3", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index 41581cc5b4..fdd9f44231 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-shortcuts +## 0.1.14 + +### Patch Changes + +- f93e56ae3a: Add tooltip to display shortcut title on hover. This should improve the readability of shortcuts with long names, which are cutoff by ellipsis. +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.13 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index e73e047bc1..8fad41f0a8 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-shortcuts", "description": "A Backstage plugin that provides a shortcuts feature to the sidebar", - "version": "0.1.13", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -38,10 +38,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 8886ca3645..7de1ea6552 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sonarqube +## 0.2.7 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.6 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index bb55e68954..07467ac1cf 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 4bd0b9e9bc..b6d77a50cc 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-splunk-on-call +## 0.3.15 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.14 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 9d9ffdb370..dfb1c58aca 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-splunk-on-call", "description": "A Backstage plugin that integrates towards Splunk On-Call", - "version": "0.3.14", + "version": "0.3.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 8943b0e461..5504cb2d81 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.0", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-tech-insights-common": "^0.1.0", @@ -43,7 +43,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/node-cron": "^2.0.4" }, "files": [ diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md new file mode 100644 index 0000000000..abb8eaac6f --- /dev/null +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -0,0 +1,15 @@ +# @backstage/plugin-tech-insights-backend + +## 0.1.1 + +### Patch Changes + +- 5c00e45045: Add catalog fact retrievers + + Add fact retrievers which generate facts related to the completeness + of entity data in the catalog. + +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 678bec25c4..5bc616db71 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.0", - "@backstage/catalog-client": "^0.3.16", - "@backstage/catalog-model": "^0.9.0", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-tech-insights-common": "^0.1.0", @@ -52,8 +52,8 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.8", - "@backstage/cli": "^0.8.0", + "@backstage/backend-test-utils": "^0.1.9", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "@types/node-cron": "^3.0.0", "@types/semver": "^7.3.8", diff --git a/plugins/tech-insights-common/package.json b/plugins/tech-insights-common/package.json index 5e02c9e4b0..26d8922329 100644 --- a/plugins/tech-insights-common/package.json +++ b/plugins/tech-insights-common/package.json @@ -34,7 +34,7 @@ "luxon": "^2.0.2" }, "devDependencies": { - "@backstage/cli": "^0.8.0" + "@backstage/cli": "^0.9.0" }, "files": [ "dist" diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 4a7cf3589b..3c2ba5da08 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.0", + "@backstage/backend-common": "^0.9.10", "@backstage/config": "^0.1.8", "@backstage/plugin-tech-insights-common": "^0.1.0 ", "@types/luxon": "^2.0.5", @@ -38,7 +38,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.0" + "@backstage/cli": "^0.9.0" }, "files": [ "dist" diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 8e7b5875a9..cef7885007 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-tech-radar +## 0.4.12 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.4.11 ### Patch Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 839ad1b399..d07b75c401 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-tech-radar", "description": "A Backstage plugin that lets you display a Tech Radar for your organization", - "version": "0.4.11", + "version": "0.4.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,10 +45,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 7331ea0147..ad7a8af85e 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-backend +## 0.10.8 + +### Patch Changes + +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- 9e64a7ac1e: Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets +- Updated dependencies + - @backstage/catalog-client@0.5.2 + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + - @backstage/techdocs-common@0.10.7 + ## 0.10.7 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 30be9c9063..332884c694 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "0.10.7", + "version": "0.10.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,14 +31,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.8", - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.6", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", "@backstage/search-common": "^0.2.1", - "@backstage/techdocs-common": "^0.10.5", + "@backstage/techdocs-common": "^0.10.7", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "dockerode": "^3.3.1", @@ -51,8 +51,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.8.1", - "@backstage/test-utils": "^0.1.20", + "@backstage/cli": "^0.9.0", + "@backstage/test-utils": "^0.1.22", "@types/dockerode": "^3.3.0", "msw": "^0.35.0", "supertest": "^6.1.3" diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index be100b9dea..6b8a8f0ff9 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-techdocs +## 0.12.6 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- c1858c4cf9: Fixed entity triplet case handling for certain locales. +- f7703981a9: Use a better checkbox rendering in a task list. +- e266687580: Updates reader component used to display techdocs documentation. A previous change made this component not usable out of a page which don't have entityRef in url parameters. Reader component EntityRef parameter is now used instead of url parameters. Techdocs documentation component can now be used in our custom pages. +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/plugin-search@0.4.18 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + ## 0.12.5 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 79841a0621..d1c337754a 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "0.12.5", + "version": "0.12.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,16 +32,16 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.6", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.9", - "@backstage/integration-react": "^0.1.13", - "@backstage/plugin-catalog": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.3", - "@backstage/plugin-search": "^0.4.17", + "@backstage/integration-react": "^0.1.14", + "@backstage/plugin-catalog": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.6.4", + "@backstage/plugin-search": "^0.4.18", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -61,10 +61,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index c3f0e469aa..be1dc658f1 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -25,9 +25,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.7", - "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.5", + "@backstage/backend-common": "^0.9.10", + "@backstage/catalog-client": "^0.5.2", + "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.7", @@ -40,7 +40,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.8.0", + "@backstage/cli": "^0.9.0", "@types/supertest": "^2.0.8", "msw": "^0.35.0", "supertest": "^6.1.3" diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index 99c33c425e..1a6a352e73 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-todo +## 0.1.15 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 5e32d84497..b11721695a 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo", "description": "A Backstage plugin that lets you browse TODO comments in your source code", - "version": "0.1.14", + "version": "0.1.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,11 +27,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.5", - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.6.1", + "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index ae3bc1c72d..a309afb497 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-user-settings +## 0.3.11 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- 274a4fc633: Add Props Icon for Sidebar Item SidebarSearchField and Settings +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.3.10 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index dc63957cb9..6b7ef01538 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings", "description": "A Backstage plugin that provides a settings page", - "version": "0.3.10", + "version": "0.3.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index a7d2d480b7..4ae5c49c32 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-xcmetrics +## 0.2.10 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- Updated dependencies + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index bbab66e455..c23a5487db 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-xcmetrics", "description": "A Backstage plugin that shows XCode build metrics for your components", - "version": "0.2.9", + "version": "0.2.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.3", - "@backstage/core-plugin-api": "^0.1.13", + "@backstage/core-components": "^0.7.4", + "@backstage/core-plugin-api": "^0.2.0", "@backstage/errors": "^0.1.3", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -36,10 +36,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.8.2", - "@backstage/core-app-api": "^0.1.20", - "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.21", + "@backstage/cli": "^0.9.0", + "@backstage/core-app-api": "^0.1.21", + "@backstage/dev-utils": "^0.2.13", + "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/yarn.lock b/yarn.lock index 597ae5e487..9b934191d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2172,7 +2172,7 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@backstage/catalog-client@^0.3.16", "@backstage/catalog-client@^0.3.18": +"@backstage/catalog-client@^0.3.18": version "0.3.19" resolved "https://registry.npmjs.org/@backstage/catalog-client/-/catalog-client-0.3.19.tgz#109b0fde1cc2d9a306635f3775fc8f6174172b14" integrity sha512-0ydcC/1ISNgR8SggOUnMNNHtsRwrKN5GX+AXgTVdZk4qpz1MqG1+fzrNld9V7KVvXdsbGDZAl/b5a7/FJEpCqg== @@ -2360,6 +2360,24 @@ remark-gfm "^1.0.0" zen-observable "^0.8.15" +"@backstage/core-plugin-api@^0.1.10", "@backstage/core-plugin-api@^0.1.3", "@backstage/core-plugin-api@^0.1.4", "@backstage/core-plugin-api@^0.1.6", "@backstage/core-plugin-api@^0.1.8": + version "0.1.13" + resolved "https://registry.npmjs.org/@backstage/core-plugin-api/-/core-plugin-api-0.1.13.tgz#583c0fe3440450c304f13dcc85eac3bd80f357d7" + integrity sha512-BWEqleTXR7m7nsCyt6cEnc+Gx/VT4vLdNr9fc1kXfzSAA7cUT4VKjlVmm5kdyih3BSQ4+0k7Pm/NIf0jfa7rlg== + dependencies: + "@backstage/config" "^0.1.11" + "@backstage/theme" "^0.2.13" + "@backstage/types" "^0.1.1" + "@backstage/version-bridge" "^0.1.0" + "@material-ui/core" "^4.12.2" + "@types/react" "*" + history "^5.0.0" + prop-types "^15.7.2" + react "^16.12.0" + react-router-dom "6.0.0-beta.0" + react-use "^17.2.4" + zen-observable "^0.8.15" + "@backstage/plugin-catalog-react@^0.4.0": version "0.4.6" resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.4.6.tgz#f33f3cd734f110d3c547f7eae4e25d14806ec796" From 587265a4891eccbbbc3346494e3209f41885d62c Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 18 Nov 2021 20:21:08 +0800 Subject: [PATCH 108/139] Update README.md Signed-off-by: Gauthier Roebroeck --- plugins/todo-backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/todo-backend/README.md b/plugins/todo-backend/README.md index fd3410e42e..5540ca598e 100644 --- a/plugins/todo-backend/README.md +++ b/plugins/todo-backend/README.md @@ -46,7 +46,7 @@ async function main() { // ... const todoEnv = useHotMemoize(module, () => createEnv('todo')); // ... - apiRouter.use('/todo', await kafka(todoEnv)); + apiRouter.use('/todo', await todo(todoEnv)); ``` ## Scanned Files From 6a2cc4cfa787eb3598c8b1da8e5beb87f4dfb182 Mon Sep 17 00:00:00 2001 From: Gauthier Date: Thu, 18 Nov 2021 20:22:38 +0800 Subject: [PATCH 109/139] Update README.md Signed-off-by: Gauthier Roebroeck --- plugins/todo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/todo/README.md b/plugins/todo/README.md index 3e1c370d8a..ad0ad4fc1d 100644 --- a/plugins/todo/README.md +++ b/plugins/todo/README.md @@ -56,7 +56,7 @@ async function main() { // ... const todoEnv = useHotMemoize(module, () => createEnv('todo')); // ... - apiRouter.use('/todo', await kafka(todoEnv)); + apiRouter.use('/todo', await todo(todoEnv)); ``` 3. Add the plugin as a tab to your service entities: From 9f21236a294afa9755673599cc0cb9435a773676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Nov 2021 14:19:28 +0100 Subject: [PATCH 110/139] Fixed a missing `await` when throwing server side errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/sharp-carrots-press.md | 6 ++++++ plugins/config-schema/src/api/StaticSchemaLoader.ts | 2 +- plugins/scaffolder/src/api.ts | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .changeset/sharp-carrots-press.md diff --git a/.changeset/sharp-carrots-press.md b/.changeset/sharp-carrots-press.md new file mode 100644 index 0000000000..a7c31d6759 --- /dev/null +++ b/.changeset/sharp-carrots-press.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-config-schema': patch +'@backstage/plugin-scaffolder': patch +--- + +Fixed a missing `await` when throwing server side errors diff --git a/plugins/config-schema/src/api/StaticSchemaLoader.ts b/plugins/config-schema/src/api/StaticSchemaLoader.ts index 48a125f38e..71eae63376 100644 --- a/plugins/config-schema/src/api/StaticSchemaLoader.ts +++ b/plugins/config-schema/src/api/StaticSchemaLoader.ts @@ -49,7 +49,7 @@ export class StaticSchemaLoader implements ConfigSchemaApi { return undefined; } - throw ResponseError.fromResponse(res); + throw await ResponseError.fromResponse(res); } return await res.json(); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index a16ab59053..34e16708ea 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -187,7 +187,7 @@ export class ScaffolderClient implements ScaffolderApi { }); if (!response.ok) { - throw ResponseError.fromResponse(response); + throw await ResponseError.fromResponse(response); } return await response.json(); @@ -302,7 +302,7 @@ export class ScaffolderClient implements ScaffolderApi { }); if (!response.ok) { - throw ResponseError.fromResponse(response); + throw await ResponseError.fromResponse(response); } return await response.json(); From 860254207c7e85dcbc62486f60f018c4dcb42fbe Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 18 Nov 2021 14:45:06 +0100 Subject: [PATCH 111/139] prettier Signed-off-by: Emma Indal --- .../templates/default-app/packages/backend/src/plugins/search.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts index 5f8ac204ba..63e196235c 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts @@ -24,7 +24,6 @@ export default async function createPlugin({ collator: DefaultCatalogCollator.fromConfig(config, { discovery }), }); - // collator gathers entities from techdocs. indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, From 4d09c602561c38501a3be0b8c7f0daf645db983a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 18 Nov 2021 14:50:56 +0100 Subject: [PATCH 112/139] errors: rework error response naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Co-authored-by: blam Signed-off-by: Johan Haals --- .changeset/afraid-chairs-warn.md | 5 +++ packages/errors/api-report.md | 19 ++++++++--- packages/errors/src/errors/ResponseError.ts | 30 ++++++++++++----- packages/errors/src/serialization/index.ts | 4 +-- packages/errors/src/serialization/response.ts | 33 +++++++++++++++++-- 5 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 .changeset/afraid-chairs-warn.md diff --git a/.changeset/afraid-chairs-warn.md b/.changeset/afraid-chairs-warn.md new file mode 100644 index 0000000000..d837744255 --- /dev/null +++ b/.changeset/afraid-chairs-warn.md @@ -0,0 +1,5 @@ +--- +'@backstage/errors': patch +--- + +Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`. diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index 4984919e6a..97b011bad6 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -33,8 +33,11 @@ export type ErrorLike = { [unknownKeys: string]: unknown; }; +// @public @deprecated +export type ErrorResponse = ErrorResponseBody; + // @public -export type ErrorResponse = { +export type ErrorResponseBody = { error: SerializedError; request?: { method: string; @@ -65,19 +68,27 @@ export class NotFoundError extends CustomErrorBase {} // @public export class NotModifiedError extends CustomErrorBase {} -// @public +// @public @deprecated export function parseErrorResponse(response: Response): Promise; +// @public +export function parseErrorResponseBody( + response: Response, +): Promise; + // @public export class ResponseError extends Error { + // @deprecated constructor(props: { message: string; response: Response; - data: ErrorResponse; + data: ErrorResponseBody; cause: Error; }); + readonly body: ErrorResponseBody; readonly cause: Error; - readonly data: ErrorResponse; + // @deprecated + get data(): ErrorResponseBody; static fromResponse(response: Response): Promise; readonly response: Response; } diff --git a/packages/errors/src/errors/ResponseError.ts b/packages/errors/src/errors/ResponseError.ts index f74ad2fa0c..a20035cd8c 100644 --- a/packages/errors/src/errors/ResponseError.ts +++ b/packages/errors/src/errors/ResponseError.ts @@ -14,16 +14,16 @@ * limitations under the License. */ +import { deserializeError } from '../serialization'; import { - parseErrorResponse, - ErrorResponse, - deserializeError, -} from '../serialization'; + ErrorResponseBody, + parseErrorResponseBody, +} from '../serialization/response'; /** * An error thrown as the result of a failed server request. * - * The server is expected to respond on the ErrorResponse format. + * The server is expected to respond on the ErrorResponseBody format. * * @public */ @@ -39,7 +39,7 @@ export class ResponseError extends Error { /** * The parsed JSON error body, as sent by the server. */ - readonly data: ErrorResponse; + readonly body: ErrorResponseBody; /** * The Error cause, as seen by the remote server. This is parsed out of the @@ -60,7 +60,7 @@ export class ResponseError extends Error { * been consumed before. */ static async fromResponse(response: Response): Promise { - const data = await parseErrorResponse(response); + const data = await parseErrorResponseBody(response); const status = data.response.statusCode || response.status; const statusText = data.error.name || response.statusText; @@ -75,16 +75,28 @@ export class ResponseError extends Error { }); } + /** + * @deprecated will be removed. + **/ constructor(props: { message: string; response: Response; - data: ErrorResponse; + data: ErrorResponseBody; cause: Error; }) { super(props.message); this.name = 'ResponseError'; this.response = props.response; - this.data = props.data; + this.body = props.data; this.cause = props.cause; } + /** + * The parsed JSON error body, as sent by the server. + * @deprecated use body instead. + */ + get data(): ErrorResponseBody { + // eslint-disable-next-line no-console + console.warn('ErrorResponse.data is deprecated, use .body instead.'); + return this.body; + } } diff --git a/packages/errors/src/serialization/index.ts b/packages/errors/src/serialization/index.ts index d37b04297a..b86661d496 100644 --- a/packages/errors/src/serialization/index.ts +++ b/packages/errors/src/serialization/index.ts @@ -16,5 +16,5 @@ export { deserializeError, serializeError, stringifyError } from './error'; export type { SerializedError } from './error'; -export { parseErrorResponse } from './response'; -export type { ErrorResponse } from './response'; +export { parseErrorResponse, parseErrorResponseBody } from './response'; +export type { ErrorResponse, ErrorResponseBody } from './response'; diff --git a/packages/errors/src/serialization/response.ts b/packages/errors/src/serialization/response.ts index 50bf8f7ab1..4a1731646d 100644 --- a/packages/errors/src/serialization/response.ts +++ b/packages/errors/src/serialization/response.ts @@ -20,8 +20,16 @@ import { SerializedError } from './error'; * A standard shape of JSON data returned as the body of backend errors. * * @public + * @deprecated - Use {@link ErrorResponseBody} instead. */ -export type ErrorResponse = { +export type ErrorResponse = ErrorResponseBody; + +/** + * A standard shape of JSON data returned as the body of backend errors. + * + * @public + */ +export type ErrorResponseBody = { /** Details of the error that was caught */ error: SerializedError; @@ -51,10 +59,29 @@ export type ErrorResponse = { * * @public * @param response - The response of a failed request + * @deprecated - Use {@link parseErrorResponseBody} instead. */ export async function parseErrorResponse( response: Response, ): Promise { + return parseErrorResponseBody(response); +} + +/** + * Attempts to construct an ErrorResponseBody out of a failed server request. + * Assumes that the response has already been checked to be not ok. This + * function consumes the body of the response, and assumes that it hasn't + * been consumed before. + * + * The code is forgiving, and constructs a useful synthetic body as best it can + * if the response body wasn't on the expected form. + * + * @public + * @param response - The response of a failed request + */ +export async function parseErrorResponseBody( + response: Response, +): Promise { try { const text = await response.text(); if (text) { @@ -73,7 +100,7 @@ export async function parseErrorResponse( return { error: { - name: 'Unknown', + name: 'Error', message: `Request failed with status ${response.status} ${response.statusText}, ${text}`, }, response: { @@ -87,7 +114,7 @@ export async function parseErrorResponse( return { error: { - name: 'Unknown', + name: 'Error', message: `Request failed with status ${response.status} ${response.statusText}`, }, response: { From 4d97ec07fa44540ea3a99f4e5ec73c279ad73626 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 18 Nov 2021 14:56:32 +0100 Subject: [PATCH 113/139] Update tests Signed-off-by: Johan Haals --- .changeset/afraid-chairs-warn.md | 1 + .../errors/src/serialization/response.test.ts | 62 ++++++++++--------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/.changeset/afraid-chairs-warn.md b/.changeset/afraid-chairs-warn.md index d837744255..1ae2a7513e 100644 --- a/.changeset/afraid-chairs-warn.md +++ b/.changeset/afraid-chairs-warn.md @@ -3,3 +3,4 @@ --- Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`. +Rename the error name for unknown errors from `unknown` to `error`. diff --git a/packages/errors/src/serialization/response.test.ts b/packages/errors/src/serialization/response.test.ts index 54a3641657..fe1cc1e9c4 100644 --- a/packages/errors/src/serialization/response.test.ts +++ b/packages/errors/src/serialization/response.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { parseErrorResponse, ErrorResponse } from './response'; +import { parseErrorResponseBody, ErrorResponseBody } from './response'; -describe('parseErrorResponse', () => { +describe('parseErrorResponseBody', () => { it('handles the happy path', async () => { - const body: ErrorResponse = { + const body: ErrorResponseBody = { error: { name: 'Fours', message: 'Expected fives', stack: 'lines' }, request: { method: 'GET', url: '/' }, response: { statusCode: 444 }, @@ -31,13 +31,13 @@ describe('parseErrorResponse', () => { headers: new Headers({ 'Content-Type': 'application/json' }), }; - await expect(parseErrorResponse(response as Response)).resolves.toEqual( + await expect(parseErrorResponseBody(response as Response)).resolves.toEqual( body, ); }); it('uses request header and text body when wrong content type, even if parsable', async () => { - const body: ErrorResponse = { + const body: ErrorResponseBody = { error: { name: 'Threes', message: 'Expected twos' }, request: { method: 'GET', url: '/' }, response: { statusCode: 333 }, @@ -50,19 +50,21 @@ describe('parseErrorResponse', () => { headers: new Headers({ 'Content-Type': 'not-application/not-json' }), }; - await expect(parseErrorResponse(response as Response)).resolves.toEqual({ - error: { - name: 'Unknown', - message: `Request failed with status 444 Fours, ${JSON.stringify( - body, - )}`, + await expect(parseErrorResponseBody(response as Response)).resolves.toEqual( + { + error: { + name: 'Error', + message: `Request failed with status 444 Fours, ${JSON.stringify( + body, + )}`, + }, + response: { statusCode: 444 }, }, - response: { statusCode: 444 }, - }); + ); }); it('uses request header and text body when not parsable', async () => { - const body: ErrorResponse = { + const body: ErrorResponseBody = { error: { name: 'Threes', message: 'Expected twos' }, request: { method: 'GET', url: '/' }, response: { statusCode: 333 }, @@ -75,15 +77,17 @@ describe('parseErrorResponse', () => { headers: new Headers({ 'Content-Type': 'application/json' }), }; - await expect(parseErrorResponse(response as Response)).resolves.toEqual({ - error: { - name: 'Unknown', - message: `Request failed with status 444 Fours, ${JSON.stringify( - body, - ).substring(1)}`, + await expect(parseErrorResponseBody(response as Response)).resolves.toEqual( + { + error: { + name: 'Error', + message: `Request failed with status 444 Fours, ${JSON.stringify( + body, + ).substring(1)}`, + }, + response: { statusCode: 444 }, }, - response: { statusCode: 444 }, - }); + ); }); it('uses request header when failing to get body', async () => { @@ -96,12 +100,14 @@ describe('parseErrorResponse', () => { headers: new Headers({ 'Content-Type': 'application/json' }), }; - await expect(parseErrorResponse(response as Response)).resolves.toEqual({ - error: { - name: 'Unknown', - message: `Request failed with status 444 Fours`, + await expect(parseErrorResponseBody(response as Response)).resolves.toEqual( + { + error: { + name: 'Error', + message: `Request failed with status 444 Fours`, + }, + response: { statusCode: 444 }, }, - response: { statusCode: 444 }, - }); + ); }); }); From ee055cf6db4d04c6fa090a37cf005cb25bad74f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Nov 2021 16:59:38 +0100 Subject: [PATCH 114/139] use id instead of title in the default plugin routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/weak-berries-sing.md | 5 +++++ packages/cli/templates/default-plugin/src/routes.ts.hbs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/weak-berries-sing.md diff --git a/.changeset/weak-berries-sing.md b/.changeset/weak-berries-sing.md new file mode 100644 index 0000000000..1b0cb8d719 --- /dev/null +++ b/.changeset/weak-berries-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Update the default routes to use id instead of title diff --git a/packages/cli/templates/default-plugin/src/routes.ts.hbs b/packages/cli/templates/default-plugin/src/routes.ts.hbs index a5a278e8a7..7e5d8f85f0 100644 --- a/packages/cli/templates/default-plugin/src/routes.ts.hbs +++ b/packages/cli/templates/default-plugin/src/routes.ts.hbs @@ -1,5 +1,5 @@ import { createRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ - title: '{{ id }}', + id: '{{ id }}', }); From bfa59576696f7d82bb8e6ea7485e261377eb3382 Mon Sep 17 00:00:00 2001 From: sblausten Date: Thu, 18 Nov 2021 18:25:43 +0100 Subject: [PATCH 115/139] Change sidebar to be pinned by default Signed-off-by: sblausten --- packages/core-components/src/layout/Sidebar/Page.tsx | 2 +- packages/core-components/src/layout/Sidebar/localStorage.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index cc50c5af6b..6248c237b2 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -49,7 +49,7 @@ export type SidebarPinStateContextType = { export const SidebarPinStateContext = createContext( { - isPinned: false, + isPinned: true, toggleSidebarPinState: () => {}, }, ); diff --git a/packages/core-components/src/layout/Sidebar/localStorage.ts b/packages/core-components/src/layout/Sidebar/localStorage.ts index 0e904ee319..78ec355306 100644 --- a/packages/core-components/src/layout/Sidebar/localStorage.ts +++ b/packages/core-components/src/layout/Sidebar/localStorage.ts @@ -24,10 +24,10 @@ export const LocalStorage = { try { value = JSON.parse( window.localStorage.getItem(LocalStorageKeys.SIDEBAR_PIN_STATE) || - 'false', + 'true', ); } catch { - return false; + return true; } return !!value; }, From 157530187ac221beae04921619d063304e833abd Mon Sep 17 00:00:00 2001 From: sblausten Date: Thu, 18 Nov 2021 19:00:28 +0100 Subject: [PATCH 116/139] Add Changeset Signed-off-by: sblausten --- .changeset/yellow-pandas-draw.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/yellow-pandas-draw.md diff --git a/.changeset/yellow-pandas-draw.md b/.changeset/yellow-pandas-draw.md new file mode 100644 index 0000000000..6d4f8116b3 --- /dev/null +++ b/.changeset/yellow-pandas-draw.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Pin sidebar by default for easier navigation From 478c4e180abb5b4060b79eb3be656afcf70840c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Eugenio=20Rodrigues=20Gusm=C3=A3o?= Date: Thu, 18 Nov 2021 18:12:41 -0300 Subject: [PATCH 117/139] Add Globo as a Backstage Developer Portal adapter --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 4f1d7fbfe8..b59cf33b02 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -69,3 +69,4 @@ | [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. | | [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | | [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | +| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | From 0fae9f2c0b0e943b089e020661ed345765b50ad8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 18 Nov 2021 21:46:16 +0100 Subject: [PATCH 118/139] core-app-api: fix and release accidental createApp type breakage Signed-off-by: Patrik Oldsberg --- packages/core-app-api/CHANGELOG.md | 6 ++++++ packages/core-app-api/api-report.md | 6 ++++-- packages/core-app-api/package.json | 2 +- packages/core-app-api/src/app/createApp.tsx | 12 ++++++------ plugins/allure/package.json | 2 +- plugins/analytics-module-ga/package.json | 2 +- plugins/api-docs/package.json | 2 +- plugins/azure-devops/package.json | 2 +- plugins/badges/package.json | 2 +- plugins/bitrise/package.json | 2 +- plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/package.json | 2 +- plugins/catalog/package.json | 2 +- plugins/circleci/package.json | 2 +- plugins/cloudbuild/package.json | 2 +- plugins/code-coverage/package.json | 2 +- plugins/config-schema/package.json | 2 +- plugins/cost-insights/package.json | 2 +- plugins/explore/package.json | 2 +- plugins/firehydrant/package.json | 2 +- plugins/fossa/package.json | 2 +- plugins/gcp-projects/package.json | 2 +- plugins/git-release-manager/package.json | 2 +- plugins/github-actions/package.json | 2 +- plugins/github-deployments/package.json | 2 +- plugins/gitops-profiles/package.json | 2 +- plugins/graphiql/package.json | 2 +- plugins/home/package.json | 2 +- plugins/ilert/package.json | 2 +- plugins/jenkins/package.json | 2 +- plugins/kafka/package.json | 2 +- plugins/kubernetes/package.json | 2 +- plugins/lighthouse/package.json | 2 +- plugins/newrelic/package.json | 2 +- plugins/org/package.json | 2 +- plugins/pagerduty/package.json | 2 +- plugins/rollbar/package.json | 2 +- plugins/scaffolder/package.json | 2 +- plugins/search/package.json | 2 +- plugins/sentry/package.json | 2 +- plugins/shortcuts/package.json | 2 +- plugins/sonarqube/package.json | 2 +- plugins/splunk-on-call/package.json | 2 +- plugins/tech-radar/package.json | 2 +- plugins/techdocs/package.json | 2 +- plugins/todo/package.json | 2 +- plugins/user-settings/package.json | 2 +- plugins/xcmetrics/package.json | 2 +- 48 files changed, 61 insertions(+), 53 deletions(-) diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 342d453eae..b1d590a7bd 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/core-app-api +## 0.1.22 + +### Patch Changes + +- Reverted the `createApp` TypeScript type to match the one before version `0.1.21`, as it was an accidental breaking change. + ## 0.1.21 ### Patch Changes diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index a67d2a2225..77eb6a85d5 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -27,6 +27,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { bitbucketAuthApiRef } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ConfigReader } from '@backstage/config'; +import { createApp as createApp_2 } from '@backstage/app-defaults'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { ErrorApi } from '@backstage/core-plugin-api'; import { ErrorApiError } from '@backstage/core-plugin-api'; @@ -45,7 +46,6 @@ import { Observable } from '@backstage/types'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { oneloginAuthApiRef } from '@backstage/core-plugin-api'; import { OpenIdConnectApi } from '@backstage/core-plugin-api'; -import { OptionalAppOptions } from '@backstage/app-defaults'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; import { PluginOutput } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; @@ -332,7 +332,9 @@ export type BootErrorPageProps = { export { ConfigReader }; // @public @deprecated -export function createApp(options?: OptionalAppOptions): BackstageApp; +export function createApp( + options?: Parameters[0], +): BackstageApp & AppContext; // @public export function createSpecializedApp(options: AppOptions): BackstageApp; diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 4c79f9eb3f..96c021b1ee 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.21", + "version": "0.1.22", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index e11076099b..b2a21634d3 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -14,10 +14,8 @@ * limitations under the License. */ -import { - createApp as createDefaultApp, - OptionalAppOptions, -} from '@backstage/app-defaults'; +import { createApp as createDefaultApp } from '@backstage/app-defaults'; +import { AppContext, BackstageApp } from './types'; /** * Creates a new Backstage App. @@ -26,7 +24,9 @@ import { * @param options - A set of options for creating the app * @public */ -export function createApp(options?: OptionalAppOptions) { +export function createApp( + options?: Parameters[0], +): BackstageApp & AppContext { // eslint-disable-next-line no-console console.warn( 'DEPRECATION WARNING: The createApp function from @backstage/core-app-api will soon be removed, ' + @@ -34,5 +34,5 @@ export function createApp(options?: OptionalAppOptions) { 'If you do not wish to use a standard app configuration but instead supply all options yourself ' + ' you can use createSpecializedApp from @backstage/core-app-api instead.', ); - return createDefaultApp(options); + return createDefaultApp(options) as BackstageApp & AppContext; } diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 6a9a55bceb..9efe6132e8 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index b83aed37c0..e0a32960c5 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 16f2d0d188..a4aa15c419 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -54,7 +54,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 0642a24cac..9d235b0641 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -46,7 +46,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/badges/package.json b/plugins/badges/package.json index dcc422ec19..4d02d64237 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 28a3c88ad1..92cd9693e1 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 53fde11c7b..13869cfd38 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index bd7f99c884..933a84f13e 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -56,7 +56,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 254718bd3c..ddee20c488 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -52,7 +52,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index d7cd3b0fd2..7228044f4f 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -53,7 +53,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 32b860ec23..4125a8c82e 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 0c32718e97..e5b51e90b4 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 0385a64ec8..c9abbc7e4e 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -38,7 +38,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 04e4f41701..da9ba8b1f6 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -56,7 +56,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 5732e3fca6..d9945edc51 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index dce261617d..f332efc150 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 8090fdc1c7..cc0b486d60 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 0e33130274..cd1c3ac4a1 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 0c52381514..7f7ff6184d 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 789385308e..ae7f32be1f 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -53,7 +53,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index a52bbfa95a..062113a8ef 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 226a45332c..dfbd6297cf 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -45,7 +45,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index aeefce3340..5c72a4c728 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -45,7 +45,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/home/package.json b/plugins/home/package.json index 74336d80be..ef13ce3a00 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index e0ccd3cd2d..6011dd4f46 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index e8115c3800..4456b15284 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index bea4aadcba..a6fbe36e99 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 8f4e3356c5..618f814c21 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -52,7 +52,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index b1e0290b4c..e53f6ee261 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index c19bc9ed37..16029f1783 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/org/package.json b/plugins/org/package.json index 035ae50c3b..db6cfe4d0b 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -38,7 +38,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 735b541451..7981b2c104 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index f3d32eed11..ee3fc9f5a6 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 3d7bde77c9..a47cee2d75 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/search/package.json b/plugins/search/package.json index 24984d10e7..aaddf130bf 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -52,7 +52,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index fd7cc34b1f..a04043c789 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 8fad41f0a8..f0139d5728 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -39,7 +39,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 07467ac1cf..f92c21ad7e 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index dfb1c58aca..10eb807ba1 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index d07b75c401..99fce10345 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -46,7 +46,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index d1c337754a..663c83118e 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -62,7 +62,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/todo/package.json b/plugins/todo/package.json index b11721695a..22a8e6b1e2 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 6b7ef01538..def12ac1d2 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index c23a5487db..f29f763666 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-app-api": "^0.1.22", "@backstage/dev-utils": "^0.2.13", "@backstage/test-utils": "^0.1.22", "@testing-library/jest-dom": "^5.10.1", From 0496f6497c5daf34d30c3a6474fd2db7c45ccb9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Nov 2021 04:07:58 +0000 Subject: [PATCH 119/139] chore(deps-dev): bump @types/diff from 5.0.0 to 5.0.1 Bumps [@types/diff](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/diff) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/diff) --- updated-dependencies: - dependency-name: "@types/diff" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9b934191d8..29145d1154 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7080,9 +7080,9 @@ "@types/ms" "*" "@types/diff@^5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020" - integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA== + version "5.0.1" + resolved "https://registry.npmjs.org/@types/diff/-/diff-5.0.1.tgz#9c9b9a331d4e41ccccff553f5d7ef964c6cf4042" + integrity sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ== "@types/docker-modem@*": version "3.0.2" From 20900440a33dff1d9a1df11affa72db4fc34b865 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Fri, 19 Nov 2021 16:43:10 +0800 Subject: [PATCH 120/139] disable snyk workflow on forks Signed-off-by: Gauthier Roebroeck --- .github/workflows/snyk-github-issue-sync.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml index 0c50b2c505..7b93374609 100644 --- a/.github/workflows/snyk-github-issue-sync.yml +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -6,6 +6,8 @@ on: jobs: sync: + if: github.repository == 'backstage/backstage' # prevent running on forks + runs-on: ubuntu-latest strategy: From 430b3f7a8f88e965587b292971b7008ff8bc8a67 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Fri, 19 Nov 2021 09:35:09 +0000 Subject: [PATCH 121/139] permission-common: accept configApi in PermissionClient constructor Moving the responsibility of pulling the right configuration key inside the PermissionClient keeps it as the responsibility of permission-common, whereas putting it on the outside means anyone constructing a PermissionClient has to know which key to use. Signed-off-by: Mike Lewis --- plugins/permission-common/api-report.md | 4 +++- plugins/permission-common/package.json | 1 + plugins/permission-common/src/PermissionClient.test.ts | 8 ++++++-- plugins/permission-common/src/PermissionClient.ts | 5 +++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/permission-common/api-report.md b/plugins/permission-common/api-report.md index cbf1877c75..043b27554c 100644 --- a/plugins/permission-common/api-report.md +++ b/plugins/permission-common/api-report.md @@ -3,6 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { Config } from '@backstage/config'; + // @public export type AuthorizeRequest = { permission: Permission; @@ -67,7 +69,7 @@ export type PermissionAttributes = { // @public export class PermissionClient { - constructor(options: { discoveryApi: DiscoveryApi; enabled?: boolean }); + constructor(options: { discoveryApi: DiscoveryApi; configApi: Config }); authorize( requests: AuthorizeRequest[], options?: AuthorizeRequestOptions, diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index 95a0d9d3c3..9e1d60f782 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -38,6 +38,7 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { + "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.2", "cross-fetch": "^3.0.6", "uuid": "^8.0.0", diff --git a/plugins/permission-common/src/PermissionClient.test.ts b/plugins/permission-common/src/PermissionClient.test.ts index 06e2ae0561..8d7933d967 100644 --- a/plugins/permission-common/src/PermissionClient.test.ts +++ b/plugins/permission-common/src/PermissionClient.test.ts @@ -16,6 +16,7 @@ import { RestContext, rest } from 'msw'; import { setupServer } from 'msw/node'; +import { ConfigReader } from '@backstage/config'; import { PermissionClient } from './PermissionClient'; import { AuthorizeRequest, AuthorizeResult, Identified } from './types/api'; import { DiscoveryApi } from './types/discovery'; @@ -32,7 +33,7 @@ const discoveryApi: DiscoveryApi = { }; const client: PermissionClient = new PermissionClient({ discoveryApi, - enabled: true, + configApi: new ConfigReader({ permission: { enabled: true } }), }); const mockPermission: Permission = { @@ -156,7 +157,10 @@ describe('PermissionClient', () => { return res(json(responses)); }, ); - const disabled = new PermissionClient({ discoveryApi, enabled: false }); + const disabled = new PermissionClient({ + discoveryApi, + configApi: new ConfigReader({ permission: { enabled: false } }), + }); const response = await disabled.authorize([mockAuthorizeRequest]); expect(response[0]).toEqual( expect.objectContaining({ result: AuthorizeResult.ALLOW }), diff --git a/plugins/permission-common/src/PermissionClient.ts b/plugins/permission-common/src/PermissionClient.ts index 5b17ccd333..aa1cde9ba1 100644 --- a/plugins/permission-common/src/PermissionClient.ts +++ b/plugins/permission-common/src/PermissionClient.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Config } from '@backstage/config'; import { ResponseError } from '@backstage/errors'; import fetch from 'cross-fetch'; import * as uuid from 'uuid'; @@ -74,9 +75,9 @@ export class PermissionClient { private readonly enabled: boolean; private readonly discoveryApi: DiscoveryApi; - constructor(options: { discoveryApi: DiscoveryApi; enabled?: boolean }) { + constructor(options: { discoveryApi: DiscoveryApi; configApi: Config }) { this.discoveryApi = options.discoveryApi; - this.enabled = options.enabled ?? false; + this.enabled = options.configApi.getBoolean('permission.enabled'); } /** From d1dcd7e309486570627baaf4801d19d9b250ce77 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Fri, 19 Nov 2021 09:34:21 +0000 Subject: [PATCH 122/139] permission-common: make permission.enabled config optional Signed-off-by: Mike Lewis --- .../src/PermissionClient.test.ts | 24 ++++++++++++++++++- .../permission-common/src/PermissionClient.ts | 3 ++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/permission-common/src/PermissionClient.test.ts b/plugins/permission-common/src/PermissionClient.test.ts index 8d7933d967..b2e66986a1 100644 --- a/plugins/permission-common/src/PermissionClient.test.ts +++ b/plugins/permission-common/src/PermissionClient.test.ts @@ -146,7 +146,7 @@ describe('PermissionClient', () => { ).rejects.toThrowError(/invalid input/i); }); - it('should allow all when authorization is not enabled', async () => { + it('should allow all when permission.enabled is false', async () => { mockAuthorizeHandler.mockImplementationOnce( (req, res, { json }: RestContext) => { const responses = req.body.map((a: Identified) => ({ @@ -167,5 +167,27 @@ describe('PermissionClient', () => { ); expect(mockAuthorizeHandler).not.toBeCalled(); }); + + it('should allow all when permission.enabled is not configured', async () => { + mockAuthorizeHandler.mockImplementationOnce( + (req, res, { json }: RestContext) => { + const responses = req.body.map((a: Identified) => ({ + id: a.id, + outcome: AuthorizeResult.DENY, + })); + + return res(json(responses)); + }, + ); + const disabled = new PermissionClient({ + discoveryApi, + configApi: new ConfigReader({}), + }); + const response = await disabled.authorize([mockAuthorizeRequest]); + expect(response[0]).toEqual( + expect.objectContaining({ result: AuthorizeResult.ALLOW }), + ); + expect(mockAuthorizeHandler).not.toBeCalled(); + }); }); }); diff --git a/plugins/permission-common/src/PermissionClient.ts b/plugins/permission-common/src/PermissionClient.ts index aa1cde9ba1..f98d40a3c4 100644 --- a/plugins/permission-common/src/PermissionClient.ts +++ b/plugins/permission-common/src/PermissionClient.ts @@ -77,7 +77,8 @@ export class PermissionClient { constructor(options: { discoveryApi: DiscoveryApi; configApi: Config }) { this.discoveryApi = options.discoveryApi; - this.enabled = options.configApi.getBoolean('permission.enabled'); + this.enabled = + options.configApi.getOptionalBoolean('permission.enabled') ?? false; } /** From 92439056fbe089749f123d52b0de07bf150ff100 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Fri, 19 Nov 2021 09:35:47 +0000 Subject: [PATCH 123/139] add changeset for new PermissionClient constructor signature Signed-off-by: Mike Lewis --- .changeset/purple-grapes-attack.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/purple-grapes-attack.md diff --git a/.changeset/purple-grapes-attack.md b/.changeset/purple-grapes-attack.md new file mode 100644 index 0000000000..a2a99690e9 --- /dev/null +++ b/.changeset/purple-grapes-attack.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-common': minor +--- + +Accept configApi rather than enabled flag in PermissionClient constructor. From 8c4397730c13f3cdcba55abbb6faa82eaffd72c5 Mon Sep 17 00:00:00 2001 From: Pete Jespers Date: Fri, 19 Nov 2021 10:11:30 +0000 Subject: [PATCH 124/139] Add QBE as a Backstage adopter Signed-off-by: Pete Jespers --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index b59cf33b02..e2585184b2 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -70,3 +70,4 @@ | [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | | [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | | [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | +| [QBE Ltd](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | \ No newline at end of file From ab7f280cb491ce2dee52773e52d609978c2927ac Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 19 Nov 2021 07:31:10 -0600 Subject: [PATCH 125/139] Fixed missed naming and typos Signed-off-by: Andre Wanlin --- plugins/azure-devops/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index f33bd3bdb6..1e7235b546 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -84,7 +84,7 @@ To get the Azure Pipelines component working you'll need to do the following two **Notes:** - The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation -- The `defaultLimit` proper on the `EntityAzurePipelinesContent` will set the max number of Builds you would like to see, if not set this will default to 10 +- The `defaultLimit` property on the `EntityAzurePipelinesContent` will set the max number of Builds you would like to see, if not set this will default to 10 ### Azure Repos Component @@ -98,12 +98,12 @@ To get the Azure Repos component working you'll need to do the following two ste yarn add @backstage/plugin-azure-devops ``` -2. Second we need to add the `EntityAzureReposContent` extension to the entity page in your app: +2. Second we need to add the `EntityAzurePipelinesContent` extension to the entity page in your app: ```tsx // In packages/app/src/components/catalog/EntityPage.tsx import { - EntityAzureReposContent, + EntityAzurePipelinesContent, isAzureDevOpsAvailable, } from '@backstage/plugin-azure-devops'; @@ -112,7 +112,7 @@ To get the Azure Repos component working you'll need to do the following two ste // ... - + // ... @@ -122,7 +122,7 @@ To get the Azure Repos component working you'll need to do the following two ste - You'll need to add the `EntityLayout.Route` above from step 2 to all the entity sections you want to see Pull Requests in. For example if you wanted to see Pull Requests when looking at Website entities then you would need to add this to the `websiteEntityPage` section. - The `if` prop is optional on the `EntityLayout.Route`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation -- The `defaultLimit` proper on the `EntityAzureReposContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10 +- The `defaultLimit` property on the `EntityAzurePipelinesContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10 ## Limitations From a519640e6742e78d2f0d6da811c71002c1ef455b Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 19 Nov 2021 07:34:56 -0600 Subject: [PATCH 126/139] Rushing, this is the correct change Signed-off-by: Andre Wanlin --- plugins/azure-devops/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index 1e7235b546..7b39dae443 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -98,12 +98,12 @@ To get the Azure Repos component working you'll need to do the following two ste yarn add @backstage/plugin-azure-devops ``` -2. Second we need to add the `EntityAzurePipelinesContent` extension to the entity page in your app: +2. Second we need to add the `EntityAzurePullRequestsContent` extension to the entity page in your app: ```tsx // In packages/app/src/components/catalog/EntityPage.tsx import { - EntityAzurePipelinesContent, + EntityAzurePullRequestsContent, isAzureDevOpsAvailable, } from '@backstage/plugin-azure-devops'; @@ -112,7 +112,7 @@ To get the Azure Repos component working you'll need to do the following two ste // ... - + // ... @@ -122,7 +122,7 @@ To get the Azure Repos component working you'll need to do the following two ste - You'll need to add the `EntityLayout.Route` above from step 2 to all the entity sections you want to see Pull Requests in. For example if you wanted to see Pull Requests when looking at Website entities then you would need to add this to the `websiteEntityPage` section. - The `if` prop is optional on the `EntityLayout.Route`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation -- The `defaultLimit` property on the `EntityAzurePipelinesContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10 +- The `defaultLimit` property on the `EntityAzurePullRequestsContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10 ## Limitations From 719cc87d2f2183a6595c60fe8d14b30a50e44188 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 11:24:08 +0100 Subject: [PATCH 127/139] cli: disable ES transform in tests Signed-off-by: Patrik Oldsberg --- .changeset/large-mugs-repair.md | 5 +++++ packages/cli/config/jestSucraseTransform.js | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .changeset/large-mugs-repair.md diff --git a/.changeset/large-mugs-repair.md b/.changeset/large-mugs-repair.md new file mode 100644 index 0000000000..5043fb80e1 --- /dev/null +++ b/.changeset/large-mugs-repair.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup. diff --git a/packages/cli/config/jestSucraseTransform.js b/packages/cli/config/jestSucraseTransform.js index 01acfee38d..c88909d21f 100644 --- a/packages/cli/config/jestSucraseTransform.js +++ b/packages/cli/config/jestSucraseTransform.js @@ -46,7 +46,11 @@ function process(source, filePath) { } if (transforms) { - return transform(source, { transforms, filePath }).code; + return transform(source, { + transforms, + filePath, + disableESTransforms: true, + }).code; } return source; From dde216acf4317c13f3a21fbed5483d068391d565 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 11:32:22 +0100 Subject: [PATCH 128/139] cli: switch to v8 coverage provider Signed-off-by: Patrik Oldsberg --- .changeset/giant-bees-applaud.md | 5 +++++ packages/cli/config/jest.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/giant-bees-applaud.md diff --git a/.changeset/giant-bees-applaud.md b/.changeset/giant-bees-applaud.md new file mode 100644 index 0000000000..c4591dbc64 --- /dev/null +++ b/.changeset/giant-bees-applaud.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate. diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index e889d23c3a..1d33372c60 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -95,6 +95,7 @@ async function getProjectConfig(targetPath, displayName) { ...(displayName && { displayName }), rootDir: path.resolve(targetPath, 'src'), coverageDirectory: path.resolve(targetPath, 'coverage'), + coverageProvider: 'v8', collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'], moduleNameMapper: { '\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'), From ab31a2c2869dcaafc8e1bb61cf1e6419f6334704 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 14:50:10 +0100 Subject: [PATCH 129/139] updated release process to not release new packages immediately Signed-off-by: Patrik Oldsberg --- CONTRIBUTING.md | 4 +++- scripts/check-if-release.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99d63cf0b4..31f912f43e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,7 +122,9 @@ We use [changesets](https://github.com/atlassian/changesets) to help us prepare Any time a patch, minor, or major change aligning to [Semantic Versioning](https://semver.org) is made to any published package in `packages/` or `plugins/`, a changeset should be used. It helps to align your change to the [Backstage stability index](https://backstage.io/docs/overview/stability-index) for the package you are changing, for example, when to provide additional clarity on deprecation or impacting changes which will then be included into CHANGELOGs. -In general, changesets are not needed for the documentation, build utilities, contributed samples in `contrib/`, or the [example `packages/app`](packages/app). +In general, changesets are only needed for changes to packages within `packages/` or `plugins/` directories, and only for the packages that are not marked as `private`. Changesets are also not needed for changes that do not affect the published version of each package, for example changes to tests or in-line source code comments. + +Changesets **are** needed for new packages, as that is what triggers the package to be part of the next release. ### How to create a changeset diff --git a/scripts/check-if-release.js b/scripts/check-if-release.js index 4475ed6207..b61b0e2eb7 100755 --- a/scripts/check-if-release.js +++ b/scripts/check-if-release.js @@ -96,7 +96,9 @@ async function main() { const newVersions = packageVersions.filter( ({ oldVersion, newVersion }) => - oldVersion !== newVersion && newVersion !== '', + oldVersion !== newVersion && + oldVersion !== '' && + newVersion !== '', ); if (newVersions.length === 0) { From 7673d7b2b3c955a9d5b7e4f7f4bffaec5e9828a8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 14:54:49 +0100 Subject: [PATCH 130/139] set initial version of packages to 0.0.0 Signed-off-by: Patrik Oldsberg --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 322929db1d..4621689664 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": ["packages/*", "plugins/*"], "npmClient": "yarn", "useWorkspaces": true, - "version": "0.1.0" + "version": "0.0.0" } From 2cd2bab86af08901a9b9193da6a2fa6f788709f2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 13:54:34 +0100 Subject: [PATCH 131/139] scripts: add script for listing deprecations Signed-off-by: Patrik Oldsberg --- scripts/list-deprecations.js | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100755 scripts/list-deprecations.js diff --git a/scripts/list-deprecations.js b/scripts/list-deprecations.js new file mode 100755 index 0000000000..c5d6e02f9e --- /dev/null +++ b/scripts/list-deprecations.js @@ -0,0 +1,172 @@ +#!/usr/bin/env node +/* + * 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. + */ + +/* eslint-disable import/no-extraneous-dependencies */ + +const _ = require('lodash'); +const fs = require('fs-extra'); +const globby = require('globby'); +const { resolve: resolvePath, relative: relativePath } = require('path'); +const { execFile: execFileCb } = require('child_process'); +const { promisify } = require('util'); + +const execFile = promisify(execFileCb); + +const WORKER_COUNT = 16; + +const deprecatedPattern = /@deprecated|DEPRECATION/; + +class ReleaseProvider { + cache = new Map(); + + async lookup(commitSha) { + if (this.cache.has(commitSha)) { + return this.cache.get(commitSha); + } + + const { stdout: tagOutput } = await execFile( + 'git', + ['tag', '--contains', commitSha], + { shell: true }, + ); + + const [release] = tagOutput + .split('\n') + .filter(l => l.startsWith('release-')); + + this.cache.set(commitSha, release); + return release; + } +} + +async function main() { + const rootPath = resolvePath(__dirname, '..'); + const packageDirQueue = await Promise.all([ + fs.readdir(resolvePath(rootPath, 'packages')), + fs.readdir(resolvePath(rootPath, 'plugins')), + ]).then(([packages, plugins]) => [ + ...packages.map(dir => `packages/${dir}`), + ...plugins.map(dir => `plugins/${dir}`), + ]); + + const fileQueue = []; + const deprecationQueue = []; + const releaseProvider = new ReleaseProvider(); + const deprecations = []; + + await Promise.all( + Array(WORKER_COUNT) + .fill() + .map(async () => { + // Find all files we want to scan + while (packageDirQueue.length) { + const packageDir = packageDirQueue.pop(); + const srcDir = resolvePath(rootPath, packageDir, 'src'); + + if (await fs.pathExists(srcDir)) { + const files = await globby(['**/*.{js,jsx,ts,tsx}'], { + cwd: srcDir, + }); + fileQueue.push(...files.map(file => resolvePath(srcDir, file))); + } + } + + // Parse files and search for deprecations + while (fileQueue.length) { + const file = fileQueue.pop(); + const content = await fs.readFile(file, 'utf8'); + if (!deprecatedPattern.test(content)) { + continue; + } + + const lines = content.split('\n'); + for (const [index, line] of lines.entries()) { + if (deprecatedPattern.test(line)) { + deprecationQueue.push({ + file, + lineNumber: index + 1, + lineContent: line, + }); + } + } + } + + // Lookup git information for each deprecation + while (deprecationQueue.length) { + const deprecation = deprecationQueue.pop(); + + const { file, lineNumber: n, lineContent } = deprecation; + const { stdout: blameOutput } = await execFile( + 'git', + ['blame', '--porcelain', `-L ${n},${n}`, file], + { shell: true }, + ); + + const blameInfo = Object.fromEntries( + blameOutput + .split('\n') + .slice(1, -2) + .map(line => { + const [key] = line.split(' ', 1); + return [key, line.slice(key.length + 1)]; + }), + ); + const [commit] = blameOutput.split(' ', 1); + const { author, ['author-time']: authorTime } = blameInfo; + + const release = await releaseProvider.lookup(commit); + + deprecations.push({ + file: relativePath(rootPath, file), + release, + commit, + author, + authorTime: new Date(authorTime * 1000), + lineContent, + lineNumber: n, + }); + } + }), + ); + + const maxAuthor = Math.max(...deprecations.map(d => d.author.length)) + 1; + + // Group and sort by release + const sortedByRelease = _.sortBy( + Object.entries(_.groupBy(deprecations, 'release')), + ([release]) => release, + ); + + for (const [release, ds] of sortedByRelease) { + console.log(`\n### ${release === 'undefined' ? 'Not released' : release}`); + for (const d of _.sortBy(ds, 'authorTime', 'file', 'lineNumber')) { + console.log( + [ + d.commit.slice(0, 8), + d.authorTime.toLocaleDateString().padEnd('00/00/0000'.length + 1), + d.author.padEnd(maxAuthor + 1), + `${d.file}:${d.lineNumber}`, + ].join(' '), + ); + } + } +} + +main().catch(err => { + console.error(err.stack); + process.exit(1); +}); From 910c2674b501f3070cb3c817fad7d855021dd60b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Nov 2021 15:52:01 +0100 Subject: [PATCH 132/139] test-utils: review fixes Signed-off-by: Patrik Oldsberg --- .changeset/wet-seas-deliver.md | 2 +- packages/test-utils/src/testUtils/TestApiProvider.test.tsx | 3 +-- packages/test-utils/src/testUtils/TestApiProvider.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.changeset/wet-seas-deliver.md b/.changeset/wet-seas-deliver.md index f625e093f5..da5679eb8e 100644 --- a/.changeset/wet-seas-deliver.md +++ b/.changeset/wet-seas-deliver.md @@ -31,7 +31,7 @@ render( ) ``` -In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.with()`, and it is slightly different from the existing `.with()` method on `ApiRegistry`. +In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array. Usage that looks like this: diff --git a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx index d3f84f854a..1b981b15ec 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx @@ -16,9 +16,8 @@ import React from 'react'; import { createApiRef, useApiHolder } from '@backstage/core-plugin-api'; -import { TestApiProvider } from './TestApiProvider'; +import { TestApiProvider, TestApiRegistry } from './TestApiProvider'; import { render, screen } from '@testing-library/react'; -import { TestApiRegistry } from '.'; const xApiRef = createApiRef<{ a: string; b: number }>({ id: 'x', diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 4408fc5b36..b1499b0cde 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -54,7 +54,7 @@ export class TestApiRegistry implements ApiHolder { * * @example * ```ts - * const apis = TestApiRegistry.with( + * const apis = TestApiRegistry.from( * [configApiRef, new ConfigReader({})], * [identityApiRef, { getUserId: () => 'tester' }], * ); From 05ccc8276c4db381bd0390b646fc4213863ea73f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Nov 2021 13:53:33 +0100 Subject: [PATCH 133/139] scripts/list-deprecations: improve release lookup to check for actual release Signed-off-by: Patrik Oldsberg --- scripts/list-deprecations.js | 86 +++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/scripts/list-deprecations.js b/scripts/list-deprecations.js index c5d6e02f9e..4056ee8402 100755 --- a/scripts/list-deprecations.js +++ b/scripts/list-deprecations.js @@ -33,23 +33,69 @@ const deprecatedPattern = /@deprecated|DEPRECATION/; class ReleaseProvider { cache = new Map(); - async lookup(commitSha) { - if (this.cache.has(commitSha)) { - return this.cache.get(commitSha); + constructor(rootPath) { + this.rootPath = rootPath; + } + + async lookup(commitSha, packageDir) { + const key = commitSha + packageDir; + + if (this.cache.has(key)) { + return this.cache.get(key); } - const { stdout: tagOutput } = await execFile( - 'git', - ['tag', '--contains', commitSha], - { shell: true }, + const pkgJsonPath = relativePath( + this.rootPath, + resolvePath(this.rootPath, packageDir, 'package.json'), ); + const releasePromise = Promise.resolve().then(async () => { + // Find all tags that contain the commit + const { stdout: tagOutput } = await execFile( + 'git', + ['tag', '--contains', commitSha], + { shell: true }, + ); - const [release] = tagOutput - .split('\n') - .filter(l => l.startsWith('release-')); + // Filter out just the releases + const releases = tagOutput + .split('\n') + .filter(l => l.startsWith('release-')); - this.cache.set(commitSha, release); - return release; + // Then find the earliest release that affected our package + for (const release of releases) { + let oldVersion; + let newVersion; + + try { + const { stdout: content } = await execFile( + 'git', + ['show', `${release}^:${pkgJsonPath}`], + { shell: true }, + ); + oldVersion = JSON.parse(content).version; + } catch { + /* */ + } + try { + const { stdout: content } = await execFile( + 'git', + ['show', `${release}:${pkgJsonPath}`], + { shell: true }, + ); + newVersion = JSON.parse(content).version; + } catch { + /* */ + } + + if (oldVersion !== newVersion) { + return release; + } + } + return undefined; + }); + + this.cache.set(key, releasePromise); + return releasePromise; } } @@ -65,7 +111,7 @@ async function main() { const fileQueue = []; const deprecationQueue = []; - const releaseProvider = new ReleaseProvider(); + const releaseProvider = new ReleaseProvider(rootPath); const deprecations = []; await Promise.all( @@ -81,13 +127,18 @@ async function main() { const files = await globby(['**/*.{js,jsx,ts,tsx}'], { cwd: srcDir, }); - fileQueue.push(...files.map(file => resolvePath(srcDir, file))); + fileQueue.push( + ...files.map(file => ({ + packageDir, + file: resolvePath(srcDir, file), + })), + ); } } // Parse files and search for deprecations while (fileQueue.length) { - const file = fileQueue.pop(); + const { packageDir, file } = fileQueue.pop(); const content = await fs.readFile(file, 'utf8'); if (!deprecatedPattern.test(content)) { continue; @@ -97,6 +148,7 @@ async function main() { for (const [index, line] of lines.entries()) { if (deprecatedPattern.test(line)) { deprecationQueue.push({ + packageDir, file, lineNumber: index + 1, lineContent: line, @@ -109,7 +161,7 @@ async function main() { while (deprecationQueue.length) { const deprecation = deprecationQueue.pop(); - const { file, lineNumber: n, lineContent } = deprecation; + const { file, packageDir, lineNumber: n, lineContent } = deprecation; const { stdout: blameOutput } = await execFile( 'git', ['blame', '--porcelain', `-L ${n},${n}`, file], @@ -128,7 +180,7 @@ async function main() { const [commit] = blameOutput.split(' ', 1); const { author, ['author-time']: authorTime } = blameInfo; - const release = await releaseProvider.lookup(commit); + const release = await releaseProvider.lookup(commit, packageDir); deprecations.push({ file: relativePath(rootPath, file), From 08dc0d6788472521fa6863032d36cf99b101cb76 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Nov 2021 15:09:42 +0100 Subject: [PATCH 134/139] scripts/list-deprecations: allow args to be passed to filter packages Signed-off-by: Patrik Oldsberg --- scripts/list-deprecations.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/list-deprecations.js b/scripts/list-deprecations.js index 4056ee8402..920b01c7b5 100755 --- a/scripts/list-deprecations.js +++ b/scripts/list-deprecations.js @@ -100,14 +100,19 @@ class ReleaseProvider { } async function main() { + let packageDirQueue = process.argv.slice(2); + const rootPath = resolvePath(__dirname, '..'); - const packageDirQueue = await Promise.all([ - fs.readdir(resolvePath(rootPath, 'packages')), - fs.readdir(resolvePath(rootPath, 'plugins')), - ]).then(([packages, plugins]) => [ - ...packages.map(dir => `packages/${dir}`), - ...plugins.map(dir => `plugins/${dir}`), - ]); + + if (packageDirQueue.length === 0) { + packageDirQueue = await Promise.all([ + fs.readdir(resolvePath(rootPath, 'packages')), + fs.readdir(resolvePath(rootPath, 'plugins')), + ]).then(([packages, plugins]) => [ + ...packages.map(dir => `packages/${dir}`), + ...plugins.map(dir => `plugins/${dir}`), + ]); + } const fileQueue = []; const deprecationQueue = []; From 07699d303fc2b7b3e10b5c4d4d769393b7c451a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 21 Nov 2021 21:15:01 +0100 Subject: [PATCH 135/139] Fix broken frontmatter on blog entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- microsite/blog/2020-05-22-phase-2-service-catalog.md | 2 +- microsite/blog/2020-09-08-announcing-tech-docs.md | 2 +- microsite/blog/2020-10-22-cost-insights-plugin.md | 2 +- ...01-12-new-backstage-feature-kubernetes-for-service-owners.md | 2 +- .../blog/2021-06-24-announcing-backstage-search-platform.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/microsite/blog/2020-05-22-phase-2-service-catalog.md b/microsite/blog/2020-05-22-phase-2-service-catalog.md index 520a2a5f10..2df8ce84f3 100644 --- a/microsite/blog/2020-05-22-phase-2-service-catalog.md +++ b/microsite/blog/2020-05-22-phase-2-service-catalog.md @@ -1,5 +1,5 @@ --- -title: Starting Phase 2: The Service Catalog +title: 'Starting Phase 2: The Service Catalog' author: Stefan Ålund, Spotify authorURL: http://twitter.com/stalund authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaace78c3009b28e917-user-full-200-130.generated_400x400.jpg diff --git a/microsite/blog/2020-09-08-announcing-tech-docs.md b/microsite/blog/2020-09-08-announcing-tech-docs.md index 98fbad57c5..f9d7359526 100644 --- a/microsite/blog/2020-09-08-announcing-tech-docs.md +++ b/microsite/blog/2020-09-08-announcing-tech-docs.md @@ -1,5 +1,5 @@ --- -title: Announcing TechDocs: Spotify’s docs-like-code plugin for Backstage +title: 'Announcing TechDocs: Spotify’s docs-like-code plugin for Backstage' author: Gary Niemen, Spotify authorURL: https://github.com/garyniemen --- diff --git a/microsite/blog/2020-10-22-cost-insights-plugin.md b/microsite/blog/2020-10-22-cost-insights-plugin.md index d265698694..139e9a7ce9 100644 --- a/microsite/blog/2020-10-22-cost-insights-plugin.md +++ b/microsite/blog/2020-10-22-cost-insights-plugin.md @@ -1,5 +1,5 @@ --- -title: New Cost Insights plugin: The engineer’s solution to taming cloud costs +title: 'New Cost Insights plugin: The engineer’s solution to taming cloud costs' author: Janisa Anandamohan, Spotify authorURL: https://twitter.com/janisa_a --- diff --git a/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md b/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md index a0d11a580f..9b8c4637bd 100644 --- a/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md +++ b/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md @@ -1,5 +1,5 @@ --- -title: New Backstage feature: Kubernetes for Service owners +title: 'New Backstage feature: Kubernetes for Service owners' author: Matthew Clarke, Spotify authorURL: https://github.com/mclarke47 --- diff --git a/microsite/blog/2021-06-24-announcing-backstage-search-platform.md b/microsite/blog/2021-06-24-announcing-backstage-search-platform.md index 0a4ae572c2..6e6c9b7819 100644 --- a/microsite/blog/2021-06-24-announcing-backstage-search-platform.md +++ b/microsite/blog/2021-06-24-announcing-backstage-search-platform.md @@ -1,5 +1,5 @@ --- -title: Announcing the Backstage Search platform: a customizable search tool built just for you +title: 'Announcing the Backstage Search platform: a customizable search tool built just for you' author: Emma Indal, Spotify authorURL: https://www.linkedin.com/in/emma-indal --- From 58aaad546e6c2f081aa79c33af2f4e07a29ec2cc Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Sun, 21 Nov 2021 22:01:33 -0500 Subject: [PATCH 136/139] Refactor script var typo Signed-off-by: Adam Harvey --- .changeset/backstage-changelog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/backstage-changelog.js b/.changeset/backstage-changelog.js index ea8ca2300b..22038b348b 100644 --- a/.changeset/backstage-changelog.js +++ b/.changeset/backstage-changelog.js @@ -23,13 +23,13 @@ const { async function getDependencyReleaseLine(changesets, dependenciesUpdated) { if (dependenciesUpdated.length === 0) return ''; - const updatedDepenenciesList = dependenciesUpdated.map( + const updatedDependenciesList = dependenciesUpdated.map( dependency => ` - ${dependency.name}@${dependency.newVersion}`, ); // Return one `Updated dependencies` bullet instead of repeating for each changeset; this // sacrifices the commit shas for brevity. - return ['- Updated dependencies', ...updatedDepenenciesList].join('\n'); + return ['- Updated dependencies', ...updatedDependenciesList].join('\n'); } module.exports = { From 3c15ad9a55bd83d2bd135081fa91e48b7c6568b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 04:13:50 +0000 Subject: [PATCH 137/139] chore(deps): bump @octokit/webhooks from 9.17.0 to 9.18.0 Bumps [@octokit/webhooks](https://github.com/octokit/webhooks.js) from 9.17.0 to 9.18.0. - [Release notes](https://github.com/octokit/webhooks.js/releases) - [Commits](https://github.com/octokit/webhooks.js/compare/v9.17.0...v9.18.0) --- updated-dependencies: - dependency-name: "@octokit/webhooks" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 29145d1154..27da8beb90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5072,19 +5072,19 @@ resolved "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" integrity sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig== -"@octokit/webhooks-types@4.12.0": - version "4.12.0" - resolved "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-4.12.0.tgz#add8b98d60ab42e965c4ba31040097f810e222de" - integrity sha512-G0k7CoS9bK+OI7kPHgqi1KqK4WhrjDQSjy0wJI+0OTx/xvbHUIZDeqatY60ceeRINP/1ExEk6kTARboP0xavEw== +"@octokit/webhooks-types@4.15.0": + version "4.15.0" + resolved "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-4.15.0.tgz#1158cba6578237d60957a37963a4a05654f5668b" + integrity sha512-s9LgKsUzq/JH3PWDjaD/m1DIlC/QWgBWbmXVqjdxJXJQBA67KZrLWjStVlYPf0mWlVZ1MOKphDyHiOGCbs0+Kg== "@octokit/webhooks@^9.14.1": - version "9.17.0" - resolved "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.17.0.tgz#81140b2e127157aa9817d085cd8758545e4a72e4" - integrity sha512-/+9WSLuDuoqNWnMY4w6ePioILBqsUOiUt0ygQzugYzd112WB+yPIjmUQjAbNXImDsAa1myLpBICAMQDZlULyAA== + version "9.18.0" + resolved "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.18.0.tgz#19cc70e1ef281e33d830ea23e8011d25d8051f7f" + integrity sha512-N2hP7vCouKk9UWZxvqgWTPbp34i6g9Om/jk+TZeZ5Z+VsKjXvGtONlEd9H8DM1yOeEC+ARDpfhraX6UsK5tesQ== dependencies: "@octokit/request-error" "^2.0.2" "@octokit/webhooks-methods" "^2.0.0" - "@octokit/webhooks-types" "4.12.0" + "@octokit/webhooks-types" "4.15.0" aggregate-error "^3.1.0" "@open-draft/until@^1.0.3": From 45dbf26094e0fdc721b1f977cc643225f899005c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 22 Nov 2021 11:15:18 +0100 Subject: [PATCH 138/139] bump mixme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 27da8beb90..788549ca7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20755,10 +20755,10 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mixme@^0.3.1: - version "0.3.5" - resolved "https://registry.npmjs.org/mixme/-/mixme-0.3.5.tgz#304652cdaf24a3df0487205e61ac6162c6906ddd" - integrity sha512-SyV9uPETRig5ZmYev0ANfiGeB+g6N2EnqqEfBbCGmmJ6MgZ3E4qv5aPbnHVdZ60KAHHXV+T3sXopdrnIXQdmjQ== +mixme@^0.5.1: + version "0.5.4" + resolved "https://registry.npmjs.org/mixme/-/mixme-0.5.4.tgz#8cb3bd0cd32a513c161bf1ca99d143f0bcf2eff3" + integrity sha512-3KYa4m4Vlqx98GPdOHghxSdNtTvcP8E0kkaJ5Dlh+h2DRzF7zpuVVcA8B0QpKd11YJeP9QQ7ASkKzOeu195Wzw== mkdirp-classic@^0.5.2: version "0.5.2" @@ -26469,11 +26469,11 @@ stream-shift@^1.0.0: integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== stream-transform@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.2.tgz#3cb7a14c802eb39bc40caaab0535e584f3a65caf" - integrity sha512-J+D5jWPF/1oX+r9ZaZvEXFbu7znjxSkbNAHJ9L44bt/tCVuOEWZlDqU9qJk7N2xBU1S+K2DPpSKeR/MucmCA1Q== + version "2.1.3" + resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3" + integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== dependencies: - mixme "^0.3.1" + mixme "^0.5.1" streamsearch@0.1.2: version "0.1.2" From 776cb9c1c592c7ddbaf0bd7336899a145405b50c Mon Sep 17 00:00:00 2001 From: Pete Jespers Date: Mon, 22 Nov 2021 11:04:11 +0000 Subject: [PATCH 139/139] Fix QBE name in adopters list --- ADOPTERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index e2585184b2..67aa8ec709 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -70,4 +70,4 @@ | [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | | [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | | [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | -| [QBE Ltd](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | \ No newline at end of file +| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | \ No newline at end of file

w1^R(?7DSzhJiDYH8D6OagiQ+84yNF|p3 z8TR1}IJBn^xU0qs43bUds3xqIdXHuwPC*GN){hr3ns0&4X6gf@`#AwR^)E_JLiin! zeWRqxUe1EUlI0J{V(s(^jpl|zh~!u2Bv2Xfq1K_?4I|zvLzlcl@%ezU4cEuSwJ@QB zK-PjpsXJ!V6)Jo#t~@~4vkBrMpI-C}{lVR3k7uPxT6{UTSdgkqS*&`_Y0~FbcX2cK z>8L-Ojv9!W%vE%oc=TX66RKdboz>k)rPf+jx2ZLaFvYrEdFz<-+_@cTZ!~7S?iu0- z3!CKF7S@w1%1p*uMdwRpMQ+b>Kj$;)`f%PJx!To%*EGJWTWkniauDL;Dc3oc;hh_g8Bdh% zry5)57eq?>&dU;~6it6#s>`z;aMp8bx#M<8y??#3AWB<4h)ZIMh<-~ba7T@G7xB4T zakg1Ru6metM)!`zz`@+d1OUhC!lgUSuU)66f)F=~&e2VI!Y`fLZ>* z%`E6i_4@UDCyNtoCia`8o70(Sa@G>;8gp>4u4h$r%1w3!Zd>$vUnn;GO*XpHwsMto z>K&YFl;vkGpqQuMR4O<7u9uBThhqG5I#ft&hpkrxMZo+VTRX7* zNychvv7TlzgwRu;wQGf9t}u<0DYdRser&`)1(O7(j8ZN#J-hM}hGIu=c1H?A*#*0VGM?>bFxe(!_r6aa}^+aJC+a8pJBdyXrbay97 zF{cV@sAdCbZ~~}aQJQiYSri>kV|w$Hzr->EpsS(F;Nox?PBCKw zxOo((GOAwQ&m?9VS-$4l`)praxXCnv|DZ3Wte5pm#Y?QpM54t7{wQO*EoamB)4S+F z$I50O?XG~{IHr>N6;AZq{*EE^J*}mKh$@|QVslxC%Pr5olM}nS`-f@B85E_8X`Kua z01bn4T1=Pj_ILDErFu0~XpPOBQ7BeBxk> zJ*mOv_1VlwFZE@*&k(P%UJ$G<)YtINmNBG^1*T%lvsjwU6${bTd8ZvwL7HW)1Z|aFt&Nn;Yhwc zcb8t?EA^ZRr4T4Hbk3VcbN%ih*Wn&$Spxt81ptDCxVSEL#{jkgHH$6PtMfD_X ztS)Ef`6YkJNaSxB<~$3qT^8?2=Q9dsFMQkorTda^7N$yXK3Ii;m*u%o$n?|NtY?N3 z!v{h>BDcXTJK2tXqHlX<$dh)2V~TvVKeu%?kgD|CcEw&+#ZY%0}JmuZ|-;TH z2Y-EZ@yT)Sl6&OA^NU8z>T$PuQUWbPVQ3-y@acT-`@6|%9y?OKX?AOFCIPe!Ch^nz zT&Ns(nu(t%vT|)tvUT)yQ-YNB8C6;FIO|+1B&p+l`0hJ={AsLy#($9n0Ob zMh=^Sp1{YvfKB0t+(W^{%c@*_36BkDg^Sv#+RkdTW(y5bXlIulz5>3ECFRPz(vcb? z+0;@fP>QbBMRa=UX0xo7rMX1WN)7iY(nkN(k@oGv^TXm&LM=Oo(7^CT2E^n`6-H4a{ZMwi6aBhUm3 zJtv!_o@CcjT3KDnD+!$k98t&3?WN->ciGa;e~FhaDffqpnYP}AQL}2%Mkn#>cCU_Vmsw4nOp@MJwKhJchHb8{!Dj~{E zHNIU89=q%A+1De*hkAyQ{tZDXv{iijb~$bGZjO!4ZIig*iIl7Asrkc_Rip8O&ppXZ znO^Alnn5~TPrhb4ro)@jz+2c}(qIUvy>8Go^3o<9Z3754&j~9UKs0}`?RMg4j6Z&7 zB?~#VR6TNt>w4X!q|;gWk$7REM(mTU#r?6alFA<^l1GXX%HDPr+PMX)?lYiclyxHN zc2q8A#$Ie~dC<9cK4hWPT)cBtvW2_A;rU|W@a0L|=uj)GUz)QDwM(6>TOlBDfH4r2 zyUJ3WpK0=bTbd0_yrOyj_=Z#cR$e{4eQak*ePVa5L3C6a4$iiz(uJIxX}scxCE0t0 zo0#v^z3z{6dRUpPHeYJ_?$w!fw$6|2O|Ecx?eCF_of=_!WnISr8lmTx`z4_MrA0Ep zSzu5l2c7!mIOcbwr2>ghc)mT!EjH^(6lwG+6OW6HO%(Lm=v2pB=J{`^OL@`0a132t zwMZknVOK}DDoVW6s)RI~r;ga-mQmA7y(x=F40vokE8EkC2X*WSp_0P41|PDw5`^2R zs=}=jOlj6;ZG0cPFW*j*F0@*)C0P>HmV@WAjf49?J!qI?nb#>MlvujX%i*GD8WhENqlJ0?fT=*O)Z_o(&TwHhrlE?4%xmG z-iU_1TuTp&0)b=^oyN0gq6!Vf72~zdTu;qjmpxEvcGEH)w9@J&SHy;taW5Le`brum zdUEd;W5r#7h5nC#D%aTk_T#(!@ugKI@m3|;ox%8$go2hk#P6&Y*>fIWJ{bq(HDeky zI#O*AW_?_gq83DpiJd*H=(UCZl|5e-bCQEeTZLHmPBPDsJNxhPM&WruY#(36_F$G` z@0kP@x}sIAw)^YMx|8AiZ0wBHbr&G1IaKuQK)#1=i9wx?q4&P28<^a}5l%`@W_81_ zFJyfa(kr{y2x7)4x46pZIe>M^Jk;%Re8q097Py1t=FN(GrblX!9Q;-!wO6MeZx|;a zgK|gVd@0833@)`yRVD+W{bA4v?iKWx@!PDoRKm~>+PfXQy=G$~ovKhz72i;MWGc{) zVw%`)OhT)u3wy81|N52&vw>NU?+P;0WxxQ?EFWjfS(U9D)NpF)wpB+9SF>3-U;pDN1S>mKB+f1ZkX(qm&BF(Q+&5J6lJ?i}Y0 zS%BvmM!PuM#C*=Iz4bhqaZ(iaR1>6~0qI<3JsYDQGepY3jwoy^%fK&$xii+a8x>Pti zw9g4X%O-^(D`18(009P!ZmT8;#kGboqxN?tFWrpONOWtDZ8tGjihQVHQygCtl|RJk z<{kn=Z&X_XCLq^*h~`?7h%F-(K$AV*rBLU+KiN_Jy`4c@wG|itElbyk;rtFxthZjN z1&Hk|db`_kdbnk)1Atd(-ukX;;}s`!n$dRsp@&H)ZX*`!p;j_BPp3&hyL3f&h=j+5 zP$dcf?73~PY;rhnQL|3cy*L{9&_`R|M&(8rPB5-deCC8ZZFGoPqT8EDo-UiX3b@Bx zxl0~nv7N`!EIjVAk`YQ+T#u-Eaz!8NE&T4mIl7_Som;?-5P;oZ4tA5=RgqYw$w_MF z+g9R@_v@&JQEKko7el??>u)zD;FszSS~&GQ`Z>zRyka?#v?%WeyS;rJqj>DYu}2t%Pk`KjS-8(}fFgzZM^M7{PX67vxAM1rKk zP4<1^8gV_K#tpxh6D(8wdSvYzBmMHihxIz);(t&UVPm*5aC zb(jLA@YAmf{Z&(37AEr+VrUl9=!>sEhK#HAZ(Nzg+V^i+3@V#2Xm`ysOs<%)WiKJ} zI%*6rbfzyAiO<`eQyp|0ghg{OiO%0~NpTQPzS4b3HfSmrZK`GZINRLZztG4rjus%4 z;ScWexW6&Gf6^?rdqn9%LCAQiO4n6-C;1Ww#g4-EPZ?It@`wvD-1Bqv(FHW-DhpcE zr4P*v9IZRH1G+x}tXw3I^+#T&YdiP*PP5RV7H^>&yxj8jD?0G9XFGb{Yo)nl@5DXU z>gMh*AmUDP+}M2;)sFhS*s6rv{3!~!W#bDU7F4$pI4JWz&M)nGSNWea6ly6Ncf=$V zo@3}kK5`D)FBYm-u@1SBt`x(WWwL5DNVqxj6iR0fn92vm5clglMbQ@9Ar?=sGBN0? zu8ZJ4vddrc_-tfLTH>G-6N>uDdzc>#mFVmV8ad{1AiZ0t_sW#smZ-z5zlI0&*Le^2 z0}gl9vcgZ7$18_brIg~jrZLU_hmTk<$$p|S+0+|zJN|o2HHc?L!BB;<#dd&j^(?u8 zM#i=4Qy|h;vsTufo!L~RsEOU*b$HU`GWR3MJ=DmC%_Aw<4}ZLoDzY~?Bm`?CC$NWl zjQR!sDMBPd3LF`90RyKvc1intIAX)zT!@p(YvJRCL=H*&>)_&Kd{j^PyAS?!cqig@%rvt-EB~JWC{<6y%AnR$a+(D*3BI)+Vb}^Vj zEzKgwkKW!RRJc~)Ky60?u-=ip`g-l=H3{5XLkcpN&=qwr8@qh;YVkyi7ec$)R9wle z-f@^RCfl=->4Lid+!yA~UX;{zxdB5c!o#GUrH zD985G1->maM4ayVbw^P+`<-l1E)>YrthaO5B};z&3k1%o7#ZcwSaH9jDz~hlha@!* z3e2s;Z zHdMB+3Y&K9zcdKI52*7HskJM-td#JbZ-B7V7&)3t!xsA-StYG&(=&?SIw9%XtHqJX zC&sj>@~qv4YesLA@iuR!F3rS#(4=|j#Q5_7Se1tUIFMD3+T!l0$!l54~=JCBrCXdRtP1DtrjngXuY8r z?5?y5m)F~mQ?=A)RUs!)onUI_9zKrABIQYqv=1^eWshj(D~PE!n#&MVZ>@$#?$kZy z-U*mh2OvWS^<76QKq|}GdJ?}ZliTCNjwxl+%G-iPdczgfJEcbxz+O&fkF_{7sn5Pm zk9(N%lE~aiB|SXOq~$61S99Yle^GGHXmJx0LVXJcY~{+^uptTN)sEkFG&+9)N+$jAxr;d=fht}|v)WcE5YJR4tkN(wyA!)TjE}Ln)D+AKo+uIHadq7gs9ns87qF$wg z*{PnnDiW!-Ew|tJFPHUFP*6a%JU#36hhYX~Y#S(811I93$Jt>Mi#T0JTsXxQ_VCWT$c(?0>)MkmsGF zlHCEOe*)OS9Alvi1(nnN6FC^v2rPE2)D(rY1Ym0sD~o|QIQq{(DnH-VZr3DUYgUIn z{_;$S1K-`awytVCnYz{{H|X98&!5*u=W_vKn$rNriXI|H^xYIFDJhLNXTI5iIhs1a z6)0n{R}zxQuiN~7i5r}3h$4{&^6IN(u0KlEHw^?Q_0}-4zAtR^3($)i#(BNQ&_Ct+bKeM1Cbg z63Psfr+LL*XgF3s2PCYir@PruQAko)PZIkgLM>ef9p0Ln7Y@vC^k2@64i?uz9qZ6V zZqz+~hOvwN3+jiloYyD^Y*2behE#4|UU&MQim3U};N)cf01vGwRP_*xwjTcL*APgX z6A>9rl$gh%jJ|%#!qU>}XWgMF&VPVxVpuybL&d1A;0 zjyyyF2eFwmHhjJ{;4nduL}N&u#%nGvE2cZ$dV5MN!uM;qO3@TK9746SS$7@VZz^&o^W{?SX)e)&cs`bBw82|c_ zMe;bFOLG9lA)-L1a+M`zZtiuwm`6=Y11L%`LDYLd>(l%2eranYgiO@P40)$v#D}?n zPaR02@P8dlhBIB%OKk=lqhsa2JBNWnDxXzgAgBBDq21bl{|W%J5P@qf)Z=(na`;|Y zG$QrE*F11AA_^@_VJ7&i*MLC0$ko&{y+Wi398x_GSE^LLYQ$zCU=i($?@W3{B(TM}R1*PJJON!e*`Iq@_s^1r14s{nD5~fC$tzoEZ+CiZ&1I6f$v;`&T)M z)fy>?OQRRgd22;v#Qb(V)NSvPoHCF3 zFgs7I*6+qkJv2y}9L<>kdGPCq4jna+P5*@-mm%l~ic9Kkh2Pms z`o1W$?9wEbSKKY7jnKLoN3^`R$`;!efkbuFH+*Ni(f(!YBXoMAM?VW*|KtX{v!|J) zX}jbIiN6K%q-Rcy>pg2<9F~hEvBp{oEM#aO9l+p)b(J zUns!PU=zZo>37{jL20wRz!KM_dcjdk;zrS#x)0`dOp(3Rb_4$o8j`u6CQ);6u)$N<2uCms5Md4CX1IM--#_QR7Q%xtxBWYU5C+wR6SB7zFZmm2$9e zk&0t#goEM{>7{2wo%TK3KcP(7>?83Q0}0cwh=Yk`(WRb|C#S2_E6Qu7-h0Pn zrtTe+b`hS$O-zj-B9G?QVUlGb9;P8$Z|PWHKn!|{-56Onf1l`$6g(07DCxJHa-+{Y z?zI6`oq24z#mgLbeXi2Nk9$%8ngr6OeY)L!uUa{EGkIG{hbn=I15tI&pprP#;pJwW zs?zkbIqBfU(;*E`eo^0(#k=Slt+Tq?hBUp-fjeu2HD!9+Nu+C};ddpcT44l~kAEEL zBxF{So$1V-UR|cV0CCp1*~QJg0?i4WR=}~PNa^eCGP%{D21!ms+_owP7?k&&d!E+g z2C507$ufipzC6{ap}1kodVRdWGxk_Lnd}lQYHY8*`@pWv0ubVXG+xXqYJ(3;LGQXo zTNVg5jC2-`>^46+-3VY?MDKoNp_Cwm(D8m$7MkEdw4qT2UKTZ(M>P~4aJHMSB+0tD zSEF@$ufzJRgeiA$nGMZ0rTX+uT85`;lk6bLecv=%;dBdp4Q8;07AgAf9nXVtGeJ;738xTejra z98j9Q{^vLKZ8LSg=E4RjxIQY*vR5VE9XV)4*lMBga&z3x!-Y5iYQv6FnM0*80+zr> zHx7kwD{J1D0sK~%nbdd61GqL8uB)m;@o zTBA=F9SBI`*9lA7p7?Fti#U-#QP8#de8j(kO13_Y*+pd4GRR`O$gI=aPUwH~g0Jo{ z-Q`hsNLVwmoXJZDfHCWH%bq1>KX7Fxi|)K8Nf3oM55k&wpZyGJgeV)9b2 zuOw3|`_TeTsdK|Hn_~;-pjYMSkx3H?_3eIf9h@l&A-Xko*J1PHlznR!nhjd-LT`wT zvzh^8?}7F{B*!0L)ZIvDSZ=6vBfTt?qRW~>qD2r{#+bWlgej|;k*&PeNd7#y?A{d{ zM`A^Nljawt3JfL*4clMp`;(;S)iS>7w=r$5$vp;^G6@~^t*hba5sl^V3Av9xV>lp5 zRjX`M9q46DGb%Xh%SbXR*u#uiMsKG~HXQs;t-p&J7D9m!F zP-`0Vqt@!jq`0*H2oM~g&!ybpL;$k~-zx@l(7Q)RC1@%@rgtc-D)zCA? zhni#*Q;M*VSLj~htY!OjSoV)QR5o7%kFXRcZl6v!$j>;&kVN1k9Ts!}{+A1t-=J!hSqr!%^!7VlBnp- zy4Y8fZr>mL`Y7cg3@NjC%(o!4qwN%GP>v3JAIX|_b&$Y|JZbeVg}ui`veTCB7Q0I; zLklKhFDAlRZ~;gds~YKcwCK17*8&&fScyo#@_Wmm#vm`Rt6;v(arDDQ4mur|c`Iq| zSS?@OWlsN)Z|kQV&3OYC@)99Gd4J*wc$1eB{WfGM4ETNU_1_KF0mHus8<4hZ|JBg> zztnX9?-!I(eD&&8V7#=%zf`Zlf0VtRPYW&nZBhew!^ZrY)X0Le@Ra_K5F6b13lQPd z2CYkfUjp64(8!4MTS(mR^OZ2h3%{wTC1U7)laWIJt@po;w;;wBe;=;@v*fZe&Wt`B zy7JpF?$U1!ic<#vltY}KP@3cU@O5{&PiQkSH5JCp3;X9#^<2Aqu77|M z2UPw^EzS#GDICCU%*{q~R8B?|Wy7X;evS7bVVc?aUZ4+q%C=_1^k^d}cYP#xUgpp3 z705?HAtJInF?&EfTh=lUdTD_Cx8if1E|7CwDUHYOOHk*l)D;0N3|3$fK!YdeU;BVS z=ybC)yr%0tYA0Qh$;vu9<}p9c1zQ^vmA&l04Z=UHL_vyV7xSSz)6NKw5=WGJ=_63u z^}eAsgqr^ENjbP%hVU(j4Tu6vw&t3l%0@?=2KBm1PO@)#(Ht6pw*l`dY&?bTR}F^z zh}{Ac@A(j!3}h*UOPout9hD4*sP;E!o1oiJLNZ!0UF-A;wcb~k4S${VC$8+GqN3(5 z(N`OE^f?cD@29+S=N? z;Dnl8K|#T8clo8Q7*KlS@7+pNo*~cV*viGHTIH!R zO#B|ObC6hW_S(w#b#w>b5Lsv2h*iYHMYT8a1@gmdwJ&`Wq!XY8v^U#hGRYYc?8>Ta zb<4q;YdaAI(l;?nZrjmkr$H8R1`OE=Pgk}+i#Q;q?+p-kytnWE@`=9WG@NW9B}^D| z`||`~>UDT$_;^gDD4C%XEhN`7va{i)U4XsEW5#+pQU;6kqS)SSksTZH?; z*H9R?Bxu{Z5t6sO5~9{FKk+MFlu_r?T=C-SdGd$OR#Hz^AQtT3nO7~zOB zJ{*UOsLHdKo|y4eV+7HL$4VoQVgyjNxx;O?k63Lj$m-R*#*r9rAD=7LVEbwMJ7Ckx zqc%UM+xeu&o3nQEraqLgaoCnIj7{8EyY0`}bsgtJHuSaU_I3t&n7CZsSN6VCyGd_@y}>Movu?@Yz{c=|GP^3@DmQE2 zr_7dk@9+)EOJ$U|7ol9Nk)_44K6ue}yeSt#Q%`dt{XM*)zB3^!nIUhaju9n{@{2_1 z@Qt?4%TsY$MvOVvGh4?gOwN1-5)BZJ0bQdz;q2}!pT@Gv4?8ku%!N{ckJBTWtY6&| z*o#I42Ylu1lNXuW^TTGW<>|)of!+Z3>FhQwE_XUIxY=;SL5jS~<1c7)?Uc!#PbVoj zKT7RU%6gAK7kcEVhJ9V{eCB-ez0=<=={Yr2eM9}DlAfUh+c7o)Er(#Ej*GE_3ZKMF zL8+@$)|_c>sJ6xW%boNB6IdRPn@nWx^yR%SNV1l;Ze;$$Uh64aq=BnnlvRyRim34o z$a2YstWbh;gOWsiFshkV4Zf>aOOb2^~p~`qw)x1&v7KTY5!uq_nvtUBp0C4 z+jAGNnO0s@LJ6K#iqp9@47skT;G|*dAo;l?_NYrfs_;}HakT2$Gv$Z1NJDsGcTRjH z+HJW(06A<3`DzfJWco&QwT@bpuct|ZTd0u|v;kgsxO|TU?2^{Ob>L23G3QFRo$J{j zyLxElBQq6ofYT6}=FHq82dl99a*YlvR^Cn#;l&}pryYfXDRn!rBt{deyahFt(Ly&w zwdUj_?rZWr4MnQ5{KL)_GDTLAQ{}M(-k=$z!M#BZ0a{fJQ~Vrxa-->hD0EA2eHGn>yzH0ievM@LbP z$Z+*b0H(%(;VXMz^nBmQPDtzG20|YVn|UzeC_2S}n&uA-jtW)Ls1Zi38y$F;mh01; z9ve0XPEXIf>F>tT6ZHCJgHoay(ecz!C^d4cbQ;w8YdW3Iy%FrK1;#MB?9tru6xG=F zSpSje=R;W0BOhqL zVL_tD83k94eIO>Pnxz`0jpV?Je5D-Pr|SPo$YZ75TU)S$F2zo>U+X7VPdQGfUD^<*$hv&z?PcCw167;XId!X3^mR*XXWPoRZwsahA1Xu%Ys1AM%kug@%?$C! zeADa$s@5V?Gf$Jr@)nkg@up~?)7++s4-0DQGpf*Z9@eyQaSiO8Q!B;LE$Jk?goeum z5wdSMNe#VNc{@cuKZYs3wKOiOe)myL)+{ zboX*KTr7h*e+rNHWx@h~X#48p)KGj;^7W&^zy|hCBHm6>t~k0SIgDNCZVZW~V$-DK z9nTe|5}}65$qm#L6{h79ZRhagY=;XArvOZzXMES8P<7}~EH#))Y$Fw$5PdRKFPm(Hg}T4)W@I`1$i-7~R=Bk>zYp8Hh(Xiv&iovUv@#4a4=!|Xj-kYFZV-TUkj z%CNgBrv+=}(30wJ7YzVI(>57{CQ*o9sO(XaJop~VlF6D}O6e8Od_DQ7t(Z#cY0fh? zb^2IInC#HA+O3e(Rf-r{!jNJ6j*?hmjYe7`wi_oV^T%Iyb}?`DX6#i*c%coDHZiC4 zAD_9Ei9*(TnBR6&&#^7#5B7xL%v^Bx6pyd&ckcLp)N%Cs-aE zCP{$Q>mls5t!z@_ogu2Mjtt)rN>*~6mo}vJzMM0s@+8<*^?=o3;JQm`qu;iHcS=^a zzV7`>_qvRKe`x@A17P4UdN(Mp*h2%h1>H+{9{f$`?B+lq zUwb_@(qLTY8Tvqm2KpTHiK5yATGRUQhO5!KbjkPzHC2b`aNo^cg{IT)qf=-SwqKK( zZOi)JMBZYggeQ$(8FPeobmu7I)?S;`3=@vkgu;#V_7tK(b>@i7}@8o@R zE7=!S#rSN1T2b+?ys*G69dBh!Juy!W1!cXjZVi8UTX(~YW|9HfX=3PV7u6fFtn9oM z|LaP5A*Bcz7Pu|}9DbK!+hn0i*?zL8_QlHB8mfUIWq!D3)1^x_9z{Gp_a=sT*DH87yqb;qOsZE8-UmebB^)6CV3#7@a5{H1++v9WrVxbBdDpS$a$hZ zY7UX8*y5KROnxl%dB=~2#w2~JxS1nzIpsb6Td#$rqelONcXz^}$r6`qg9=bYxfzAi zB38jss~6ZWcD4s^uQCD1>dy$5MpmChyV`Pnl7grQKS>-Og8f&F8}80cE6drOd~HeW z#!G<1I6pu(nR~YNY2UPzY2i#38`Y!Jr_+(TEG!Q~%j=b2E3Z}M_=wY+R*^{-=u{Lh3VGiOR-4| zogQ%0#6=pe7nJdn`ae$_ovBu)E1BCfy$yltRt01dJ6*EZO0XrMFXXXHpV|Q$*z6x> z_aJ}z8$HC+``F`jRp)VjznGt`pY$ecfRE9&TvVxwjO_KIps$fN!SCFkoq0C3Ecs`! zQ@WMfXA~9%fWyvqnO55rjVKq7M`{tGG=+)pdA954$_E{nu~)u2U>nr!4tdw~A!2|P z*GeXTfj@p4#^%OWMd-@o2dY+}sC1-!L!bE-ta200W1>!6IEr{%e?57g7o4TWYJ^AI zu`b8$l(5`9q&)Q{P@l@bzOtG_1#gLKziFf3c$PV91UZ@xoVX&xr2*GVV%*^EGUyp1 z$?0-p<@tmaY&H&nK*|zzm~(n&odCnuq(HU3yGW}ERMHclJ8AO_dE_#cd?Qys#6+qk z%Nc%Rt_?3QyhKcjvlx@(O-_I_W09$1%(#3tGGp#xyv*iE?30HUL5AARjl75bJjP9# z_pl;bvF`4Z$M@oSW$qabRJ05q?u*`=+_Fu_nebEf5|sC}9y27$z+7Lun8*fH_J$T3 zY}|o;nz|%3Ol1zf@aGy=sjLOiMlWCjaO=>v;gN~H0fB4Bz5F(Fw|3^xHTB7 zKM}oqvUYm#x*MW=#qBjgqZMf~XGD{Rt3!OiPGv1SmaviClh+v5u z2W_d}#}pFp2*AeHz@Teu!zR+I)j=<@b}WJ^>+Xw^M}9)&zT<@)!5={We6jh`>9b`T zpl_#$^hV}*+eI+&JwfRG_0D#QihH0+bh6laUSqN)&qZEwaSY4K*geMaoE;$WaS7Z0U)jN5x7yw-;`n;98L!;}iENHZo1 zhor_r8oUwWRYH}Qm94;mO7Bn5RYUxmc%#SZ;Br0SF%nC@t9trEf0Ukk{#aoC7C%k+ zO{yk073TAKEd>v$y*88;Z_3*WXFO(CiYXIz+h}P>Zv&$>_Xc@^wSS&;qabiZTZ*@i zn_{v)MfQ)@pw0KpEtIECES?Yh$jA*4*Jf5tlOc^??Md=;MP2Al_g@^WX>@c`EyDgx@5>$ zs&~s42x(WmEn*zd;N1V&bkT~MbRU(tC6%LMq--CHnT6)nM+0G(R7K!vq=!$G9b97GOi{A2slXzRqz3-X(_2ys^%W$PIq;SUX<7rCwjtiqa!m zag0_(4XIZ-d7B#1?NN{4qBoL+dNR6s!SmITlD9K8F=0ru`6@Am*U$MG*Adjihgbq( zU^c1MIw9I_m>44AzHwstWd`Pk*r0vQWO3{a(etf&Rt&*aPK@ESC+m~nY0!H z=va(k_OaADH>L9Nm?=SZD2vvjelm?p(GW##O&IDpkGPxF@jb6AfnN0LyKccgj8R8( z^~3b+&5QKLYZI_Inu1*Hny*}_-tTn&*L&8cf~dPkW%VK5aX1MzAp?Dw4Q< z`r5D9D0WwQhffDH+`c|@YhxP6QkjbbMeo18n|M;mY~|)tWEpP2K&B}C+_+b|X^+cxa(#@2Z8eF}6 z^To=&1oO|Uvtu2zHyr{Ui(4Mvp;w!)YcwgFo7Ag(5|FFLHO-m>RiLm2>q0*-V5fg@ z%lTwfh()ac5nM)-tyC5?-)RsH&B=1Sg5lx4D z!dt8W;{*Js*SyO<2lp2I{PtGuC9SK1sOv`|;ws2qQ;Fi{DSRPd(G~J2uf36&xAVa>{*-5py3Qa@MweW07?c0xXB@zF;me z^CE8|S!fZqmhK(fJ1I$+yle2C(i3-?vhFSe{4hpCI?v8V)ot}cM0BK&qbOUCd*hgM z*|!SqhRoO2#&F>-iel0vf#12-{}O8@^&;{KAP@=Z7Zi%Q*Xk!Uj=usieLB+Dg1`k+ z3E%Z7+Ea;zXB(0@4rZ)I@$Z6?UnjVxC9k4T|3U5Z`Hz*?lJTF`B6QcdTWqjjl%Jt_ zS7gEEaj(ncnA|m!Th}0ZvJO?up*^>pTyugL_5=TP#ThF=NL3r@s-apuy13r~`CoEv zoqq=02G8Jjr9DdD_b$%Q=BlPtDL)wHeIvf;ju_*n00{FHWeH{p;=e>_;Ez|Ne*YvP z`|r!T|9h*t;BJ6_GLHZ4lK(8Y%#AYxTBdY*;o$9NEd9m_3L^jXK`A`GfpR0OEkB31U0Z{L^0A?r+cLWhh6sV%2K8Ax5DtZ*R|8J||UJe~V2c(F2lT*J= zQ|w@Y`~ZwT%5^*Ry$#Br+(G}47%&^a>N*cKKGSc^h%Q-32$Hn*&`T}UU}#5!06^EG z#SAtz2z=MKfFYss;PGJ;$8YQQ7eJg*#6JyNJ)m0a6F&Eh83QHII+!~c-QWCYB&vdl zyyWEMFmVlA;86GF1dW)xX7-0)bO7?9^DRU((^-A~;13e>dLV(Rl0oBp{jr zvp?Lqc;PR#GPu)Y?iAdb++0~K1oTuY(~3wM-y2j>iUu`bag2ZaAA#*A4ZH#Tmg5<2 zIH0yp6in8(0}`s$_AC%R$~`sB_&o00R<|AMRQI zmqg;%eoOQ`Jm7x{X@9Zfb0-kh-f7$UmiD-ZM!UBfDw_PrP!9pMab(Zxf!uGtSfF;# z5i$y7K~HkMioW31C-=?s-zUeJnGtCF?I5uMK2F9Efq5fHYx?vuSgUsjDgJ?%xN!`B z6_7akpNTVn=`a6B9$vq24~Pr@C+1)OEC~Fn;QtqfPXFg@8u;yr{EsB4;XHN=Z8vyA zuYlfVZwmiO5myim|IVe)gQEUJrnoT&CsuBgm! z7O%}+-h{}z{yc}$dZggQG2LpoM@-l_fgVLx&2Ku3ifwlScCKArEq`LQt%|DOM1tXh z^&>%w_9+)@)uduekY1N_Tr?=Vn|2Q*Sgj@)DcrgwQ^ni3*qqw0n0nX{BgDY`x>xK( zB&~$$YP*=G-H4~GFhp+>D?XjMR>7!%h8@1MZyx>^u;s7CT78{O^p|%c)~O_(3}g5+ z%~QrtuOf*7=v#Q#!;xkApI97QR+}fTt?`c!ZwVWwo{o@AmX^H%?qaoP5!K^0T9;t;JC)G0$xs~TFptiso`T8?TfH@r2^yW_tP7Kiip7f(u&yc#KP|(p z;bz6Yo#ckPTS;9`hb~V#(!zIiM{8zWc_rX)+ITsEgQMr$39njdub##2nz!CjvWOR4 ztBSCWok^IJpRU+y;4%$xRtS=Rh32&o=5?KD5ibNyg3{Q=>NB4h&kDs0cd>`z#ycD- z-#&AX&nFQi3f6pKMuR8Nx8mj-w{0@fncBX5)Ld4V;ubV%3Yi6GFp01171%`c%BLxM zwmcGTwNaf~kbobttz`&9%5N_`Act%%C zcLVWe1WsVLPDOI}+o)|H=F zj{W4#yVR{c>1Q%yUXDu#1;QKe^EP5GMmzr?vx6)P>8H?d_hatST9F@^M+)b-mdh8I zvqiTIh!F`!a`qBXVhZ(UqGoP{#0D(dQeNiR?UCF=eTvdssPEZ47#4C&*;&Uv)Z+{` zZ`Ee8?+9JgP;Hwxdsp_QqY-r@KvfqYzs#D4)m}*&n7DuOfdbzQGaAT6Ew-ZkIfr%2q*MuygRYdes;U% zPOP9OtEqRPLTZBtl2E$g-ffFFdaR>!laXQu8%eZNLd}}R!H@Tkh-x?~j!DdcpM5FY zrre*AR1h;TCaRNH#NN+MM6N!+G^O7ALikoLJ~X=p#k-1;P#DZ}cwt>V+y$Lm zpU|@+3RF-UeuUNTZdkjHj5`JHp&9CW1tsfwI5qUA4_Y5#GMvN))Kc3tTES^8X&|#; z>f)S2x7~V|z40fl1y33x6WfNA;?!BxnUX%|G8^Ky@r}57y=tDdQrQw~y1<<8y-B9S z($H#`Xo^$)N2e*AtQf7u_g8!DD(wV4D=t6PQ|dH{QWMlUvPYHsz&`@G@X+IE=Hh6! zQ{4C|9n+9}k6v~@WiiDml`$=A+Q4@Od9i-t;#ov-Q#B*ly5)z`#3^Ko#bSPqZe`-M z7@?kd%yj)+8Qt{h4)^V~sfM+$;lu{>Ww`N=YIRxJ%#Z&9UOt2Cv|P>EiGFuM;Pupr zi#>KGIVyE$3eZibB%QQWOJLp?OE!WYKNi-!V>juI2sjgdIYAav;x>ox0C6D6jYUt> zCJ@~ntl8VeC;ho7IL6_q@2Z$g4@Og|GzdO!X=Ouxv@ytj@@`z~8;nXdIF(hUH58kQ z7&qH@r^xsk#xm^qB=)0+=+yCGaohKorzLAe)m9H&<$!$*lNdG$hMQi0f=Ofu6hoFBsE9BB!^U64OSi;L*sdMO%r z!0B&XjI<#AAIja*dSrg)IrO%cY#*!K^KO*_j(Qp_ib^d@*$eG_;(`+a=D+V+n3^(CVtwfyfihOW9E9OJTt;yXlxKI8*I(@VBdhxty?u`>0p`NtbCO5=Cr zDy-=y)Hp}!nzQ@@YeH5UGgco{=KOd8f33=xrGxW@)n|&P(LQr@j1R1cez{UxYWFn& z*^iSm^@Z=Ag5CGzEQPMBMDK=2hTG54Z>GRvKu6un>PFOJ4l$p)(uf9A+Xhj30d&ai z-IT3*TNFq7z1D~N1ZG^h(Nw!TYAxY!tvS?WN(^+c5c>+}OSV0>da6u5soba}tcc6E z3vfkDfB%@Is&%IH(24&M4Sx_8iV^}+F<#uaU)@Yf>NRiDdgGz#!|%~xL@?=Cas3Sb zQZm_tpZsiJ>Rs}g6rb*Ojvohj8zH5W*ppHI!0z`pB)uXue*msy4=~%+ZFaC;mk$J{ z<4~UHj9Fs30!WCLV86}knBtW| ztnw76_R_zEe_sF@giJ2!(;9{si2;o55S%m> z`Jao^)!ZE-Au{Txm**!k2$9gEtPe(mfYW@7#>Vd-F$3lOo=nYzNE&k z7)?bqE59AX>+mRAV1IL~9bb*(|!hu$`FlQwncB`U+ zE43>hXfXt2;$Z;+^!MfuiEv}?v1z!9x^FQOs zl6`F;jECB(z$z@7AT<)kbi88|ipi5a<0CoZM;~nqpfhLnT#tiCce@T^qou^MGv?}^ z^?QKJx3GvN1-}(@-~q2Z1|rKLnn~mNn1}~_Q!M9Q*~)~_aHii7`k0DURm;%W7q2(GNx-6tFeg1x;i5=0VtkFs8{M0N17*zr7xH&0<{sz#~~ zbkaA|WFJo6=CX;uhdnkAX|8@VRaDfki|+p+sI*Glyl8J6vSE@a5Vx8Y;uP}j-7YXN zEGs3 z_2N__;!g=(7l?>s>HWGU!;(w|aiBzcnk#>CtCU*oeV{Hl&e2JTx^iujNOOw-TWx9n zC=1Q(LEWF^Ha-y>@L_tO#$?l_$%PRp9I>I|eAXMf0k{8<+qr3C;uuQ*^2(j%3m_{~ z_Zd`GuPLbPz9t^oQ%ya8xT5~RR$GDp80_*YazE15l=;qY7cO*+WyiOUx7$@5cv6v_ zG*^c#w%?re`>bC0E-bpoT-WKbMQDHAL$A)G4h^c1Hl@MQvgdmR6j5!UvdsmEBz};6 z*QB@<-MQvf#-n%iOi616B|5<#sFrJnaI)fh3(V98t8%?vVKE@X?-aEXLK@?&;LBUD z{p@9HulKf+Hm&_su&HX6+Emp<584gD^&Skx*NDl@d6|5efQo5WQ;G3~Y5_0Hn$0vB z?H<1@tBMJkzoUB&#-<`gX@Cuz_;=cPD94Me0~ohYchdO@T89tbMQvW^PF>aJj` zcc7NKv+NJhqM0*hgdak2?(Ea3st3ne&ONG=edoS^tI=ukzE}Rt@Ig+)!7z4G4XKzs z)LOss6GgJ@i!1txM8Vnbe$q2NZw8FG!)L7D-WAu{ygk*Y(Ta)fo|n1-bLT(UuOeWyYdW}QYBByp zUu@SuK5&s%+9g)?E5tqyd@GHS%Ai=r@y4Rr0z1=D8bYmXud(k#%FWBE*(+K(z(2Bg z#Cp)w+5BkQg~jCcWwSdMPko8<$k9~GU=Wu`lTJX5=(DK~;5C@8zX0fVlb21&7ZBfvt zQJfIp075_~xntlB#kMkqP+R)vPf@tgUNaUj&t&@;oUE-(bA@cGn?z+D&$hPZ`PgVav2i=HrB< z6x_7(rY#CuzCx(UlA)~VSQ~UPZ_#g`_vuF?#fXI#tnmMwvj%t)OF(gKVqZz|Z_L&4~V>Abk0f$zfLgJ4d1 zPosRVd%MSPsd$gbwRQJKCmO4T%WtyrS zHdGexeUy_nkMD%JRi*xdKMuUbzFo_(0c8W7tKyBK#?KrOwHwq)Y1!LE+X(AVy26io z0D%?~AgY^W7M>G1ZZF{*XB*e##>za#|C4$}J>jA)3Tr{oqF^ z;#f2obV7dNbafp&@=<*ag=<(8ZPR~!0C>DrQp9j7HYH(CM-nq#>DGYUo3y`F;}3Og zCi{f9e60um?T;6CfeGaW>6Roc)6fg%K22*@p7%W;W`d)q`K%5eRg@_58egmxa}XJl zxnGwJI~&g^9Gb;ms1Lff1yx}?OfcrQ&8X4yt-=r&RJSmu0Y>rYi}-1d{a23x5<8XE z$d7~qpBY#HCpG_$9Wkcb*oph1?DO&w(BQaN+;^ap#u{(+TKum`NUPrkdFZt)E(b+J z$D@hd%a1n8J0UPTzLZd#| z%Pgs5D*DsAwIARH@M5zmh-n1i(Mh`y%cj6O|CouQtlV-biCx10l~q{JzT{D<{C3>D z(@$Z$#9h6|%F_55=2#EouUJjZzn^S@kf>b91e@AmpuxjCe-XBlvi6ty&@aOI9r~4b z)ypt-dMj&pB9pmYMpd<-ABXH$e?qI9WNDM@=P%zR(akfe7y$T)#B1^6CA-|>URSnF z70bVF;g^@VS?5k5fwhnDtN6G#v!`qXD-Y>C>cVJB)jRf>=h!lM7#%QuzT~k5m0;W# zGiqsaX#KS3VKscDimv*U2Y{L~T##_Sg&wCBmU=ISC2VQLBIpH@%s1xiu?wWP)1U%J8`P>VG?G#D zIh86?oOns7^-l!-9mV4FFIrQ-S9L;!V%^S0r$N>RL!;KI>=p5UE@mp9c3q(!EPr1% z0a!&x;?->*Qx6N?*T!Dum~e}5j0iMVzn;JN}au6c|%6T6*aF%CW}4>Q=8mWl(e6m|8a$TZ~e+Zx*8^BN@Rn}u0YH4e7N5sqrDKh zxVnwYGUo7LyUv^p6Wz`81R72%B(0u;Sd$}w+eMd)^RCe3_R*C1@4?M6kJ)bJSDd_` zAg=`Ak4G!+-w&5I-Mj9Z{&*>Kv{O(u0%FZ-#OXJt=J>;Ps;GBjwS01ThC6I=QXSRP z?Y1z^|8yY-t);sOUJxYaG`l215)(^XFCaMU~nl@B&QEcs@?%1zeIIsN(;K_a4XLzUx*+lSKb*RS)ClQOKCPG5ShDJGPGSW8{g{qnS*;n$#|O;ojmVmlCwrC zEH)%NTG_MybTZfW`aEBh+@6P!PM5P->&dj~)%{6a1o9Ks>cPt1+Hlt@O&B|eL@X8< zYL(*je6mn>RncFxH~C+xUY+j&uKbPh7Wwubp8ZW1Go~e4=Tlp51)Px?H#kyADuvBMK7YXF*c!G36D!tl!dwH;^XT5jH@s_$ZC5WS$=#B@D~ zYOb`zFngt=5H-S=k@lo2!CZnw5at-LTbJ)Q(W8*(MMfn&%uQO`qMR2*?Vg0{q=XJj z3z*DzT+HIpDJuMKEJ#9qja3Wod*FW0#K!c*h4)c_#;}It?c!8}THjRMae(e$VH@;m z{~7-II|y6oy{rj#Uu#y>vzB7W4cd91IQfrt?~KTvQDrv%vaD}HHE+e#r#1Js7-$3b)d-caZUc)FbkO_=qM}hqH@2|l~rhAad zbE|nii?}2($@)3=J;#!NZgh_S3ZEC7R}Is^?4LUXXw6lEQ9rKR?aYf{+UhsY-+=CQ zGLDYru7jQ&930ShR#q`tSxkb^-TN%Czk#5a)j62^D;5CwI~c%08=^@dW)0f#fmPzI!iB8SWAhdynOevi}YXJG?D)Uw;z2^Ph_4SGwe zVs}Z>p@g7Sunv*;O)mYUt(3;!$wP*UG?ACE$z{W6U{8y{+vgj?8;@vJzk#OQ>R@{@ z0u0wm{q;y8pSb363@P@{?J37ix%Ts*-*Rcbu`=$0b`QBocuFzo#8Utbf&N_oLd_HEq8e~t=r2bDh!&PM!iJF0}GOlI%Yy{Z?a$x-G|B~|qU}BHIwi+S|?Am{@Y~X<8gH9E4SxP|a z28zH!}EdaFG(EpY!x@66S+t`Erch?m#xP_V$ zvJv&r%SScM88o;SA}C83bT=jXl?VnhBQ*e;R?vXNSmCd81N$D}ye%3=qqTqk@{jL+ z{YYZs>Tjp@|1Ku`yZ!(F68!m(Oh90h{QLO-cNp>im?HkoAEnlgze!ZDR6pC1F^e+o zBqb|cc`=y)I|K-p9;M`Sbj^Tr!6 z?o+Ofg`M5v;ZE|o-r*1d=6hKIgmwNCstknwE6}Y3<;T6LBiD_WXiu+xQWWIA&U9Nv z-y~+N!??Sis@XX&OCQzxw8UgZ3VDM|wj2^Pjo%`sG{rD}bRUeatG4~*>Q#v9#xAg( zo!lOK&>*A88`wWD>AKt#DwRbNMSx6rdR5x^gr2rd)X7KL=-Kk#?KCkzUVslEvtVxD zuI-5cjkAwZ)KLdskF(og*(q;m%G8g0n>_UVYY~01DR;^ACr3n-Cr6-Ily`4&vEmJ} z?P3dFGNY86E@#ECOLS3=CmaQ%_s9&)3=u2+Ucc_6+^?)5JO_okdi4-0=uP_sRlCVkfX#6;A=11gVWUEA7a`(ALxb2}@U401Ko~!&n#JzP~ zRPCeo3nHSRU;)x73ewUI@*sjzN;gPKcgI3el$Mr8x(66)Xhd@8?sOQs1{h$7voM~= z-~R2r-+kWmo^$*|KPt2GUTfC6uKRmkh2cp`^dRR(V!yF-(DQ2X*!2+S?g+k1qANmZ zFYYcvQlUtD>6QS1+ueG@`jF=qF!B`3rwgc$Ewyx5(#R5afJ!qN(_-nU9^1PXX-{|y zmeUqI^le2XK~@Us_I+-t{b#1XPA1+KI1}K!HE1XzPKx3hjHCCaO`xzX40@cYadbZs zKs3GCvqjHxD6Rd$o-(0`8>ESa@=KBW}x1<5y z#u@0nWHh7wFab@QgpKCv$nn^b=!I46Z)N9jn?UM0+~#=>w^dXW4Q$ItF37I*LhUQ? zJ+&JBY8Ns6=;wHuxcw8&Sz-~%u^39OW^d#x?BGw-x5NfqUNNGhM-P3$GbkW0t|3y37wTMd2H)WD~-nQFB-=k*(hyGr21pmTSRi+HfCN8F^O=M^}!?NbI{ z)U&acc;hKHXpx9=3ZGH(DZUu-+O_<%F*kdZdPf-6Ck@>#HK%>sRf34I!@&5A4p`mf zTs4g0LGb=O&!9!}m$RESq!)W%3O>LR_B^nS1%%z~jpD%Xr63AE z`TfnAADs5sL^d*Ik36}0qSh|ZL2>F3r^3l)_o-asXkt9nW<9DTUKyWsLf&ofRanVt zD(gR(n$9B^>n-nmriRUr<(F6ZUQ$m>!PhPA4l*jfGrMeXMNnN;QHH47Byts;^s*4W z5N~+`tVTI_v|r^~^{=pTfl;xjHul_y%v{RSJ$6HWj{83KZf`BCT}(HQSxZms-iNO6 zeLK+s9&plH3`s}2zNNUGDBMH$bu`&PO;YBID?Tz3op@N?~XH0cVho7ilryHTMh#bKu^_(L|(~Xtf8s~u2KEg2)~Y?mnxzy z8mAIS^PED+w6nQ=jAYKLS!__LX7=#OS9h!GS5c|t#*(_s>TWl+VDm(m<5sJZtizo) zEZGhh+}&gNe2NH-tIJ002EN6Pl~l)hXcjC}s`?M1c?OcK3^ z4O7HV^UjmmcILJxWWO%aP;Mqrs;6EHQh%CnA3H+HoD0BV#UdlflBmSucNMky$mVrHEESu%Jc;6t6?Y*NXteFI zPdn&Xd->_$rRph-T>0$K^%R-3r{x?wRfyxi(Hiyox7k*G*6T(D@JX)tM?M}CZLjkQ z2?r;N;eYRr=j$PzJb%8{tuPy)7hyDytqpc36rUX)XI$+_yl~tzKUwKU>|b%HEDZmzNwOH@BW(>)dbzMO{ncceQ;Zt2LJsdHaW zEF8@y(zHt{A_1lv6D?n{KtGm?q(AI3EOGBe(uwyx!HDk7v%`19yX<=itgoZC{!{Xd zngQ3rvO#ru*;`&|D)NZ;H*t1k!iD})QzT{7a>#Isyp%2ha%LcA76xKwc=Ren{n8Ko zR$|lNT3A15CkL{MzVBnzy$ndOx@8J?Rf(SF?yuriYgdd#fUcZb{k92g0z|umDDPR+ zoE&jld{ZsV-mb{)sMSr%wKd#xEMSYOC6dzG%S8`XEod_hC=0$m(Ik@2!S4?#h|oAX zWLjub)OZ!5MI5;{GW-KIdmHbdZDx&pq|FUL814#2UTq?;S;kN?&lW;6F8V>T11G!q=V_9q6H&>i;NW1A@5k6K ztv|UlNfUAYDR}qDW%hQoh92@*u{+{k^-G;sVUh0Fws&oeAwz-kd*ilsTo@=hXTLd6 ziI>8xXqFuD%s8I64kBW-5H8&++p0S`Kv+7h|a`Gvy^GP6P zQh#ChQ7&6cmuD`gBaF;yQt&#DHsG&CUk)|N*>PTpZMZpNmc1uGrc<-0mHs+HkjDM3 zv4@M^#W#Iq;G;hqSYhW-48HrxZQpliGjJmgRPy9AczN`+>nCvLWu|pA&?^{~F1@D9 zRk{Tyr0D=~-Q$wn?Y>MVotbKUd0S)SeILy|ZuW6Xx#hIjP%QET^&z1Cy`Rg#J+e@D zVQ-Bmg50PrvC+Q zh9ZY4D$Z`U%N?0IYQ@BKw6&GV@nqiwd~?7HJ}y2LyXT4W3t-@yNtJR@pW5Ad8Ej!? zbXBgqo{Edmdq0{y$0pUH5^%1r#NNlF_e}hsNsd%|mDHl+4?iar9>KOJHp*A{F~O0F zSt-O+f8Eo28p6!eYNaSRX82cbLt1PwaeqT6)FP2hd5jU^cM2zGL=jU*aEmIv8Gr{S zU?N;)LF3KgF7V5FvochXwH;;SCampupRqecFF)V$VroS9foDamZCy9-aHS`}@P@|1)><;Ks*=2K0mcRimEz_I%|I1DXLxbrkuOtDo(oM& zkpaPm!-Vr0lF+!MC0EO~W3)nqRAvm&U+E^UFMoenf;fAj2MIh7Oqg!YrBSUmRZoNG#}m z7ono${>hr@Q=b;K!=k;OcH7eUY1S^%X{^_@Jn%K}fVKo-3)fc!Ss$yP%HYyOtZBj7 z!zWaj7Dz#&Nxtg)ugYPum6i!zBCA(;c^^7}U@_*1+};bu7wGvnH(75G?|Wr- zrf)0ugq{R1%Jd27;^1ISbt6>SFWCe{B4j0_qfB)9)`|x@D|46gtJA%6nZdhr6W)Bb zmJwG9pe==s3TwK`Det_fQR;P+(emdAWnubTS58}k2c ziySQ9R%CyFiA6K)FL}}n5|Y04UvkovjEQ#@%N+8Q9CtHDoFAM3qP{sVyd>;CH$XtN zEQ6kiNf25lML-*|bLrZnA8?t)GWD(Q*ct1!bP7tV8jazMeys|+cPfS0SXCFPW~FUI zLMAtbA_y-Dp^xo0(b%*)e^JoH9lY<3LkO!PEFo{6C$uoOuW~yL+tSRjHMek$+9A=R-E^Aw4(wgrq(_A7L)3VAGZKUoObFFyOqvEP>t!FN3Lo zxRiZr(@*j5zg^SDqX@wLeBm?oDsOs>#;C{Zf?l62!sxhd;6kHb?a7;}-rvt>} z6S$ilUjV-uGs?=IevI=bdbc*2agBaOgH()`Fol`ltV6|wtxmbzj4DEeiIYB#FXz%< z_hc%Z2F2{5%O768*#H3EjEKiBmN_zzKiYN+(i@xN+It;(^S zK$w+ncV)AT>8VpU=AuZL-CKUS)0Y|Qx~Qa@SJ9N-IijGy10 z){LQjm~`dVzPAdt4$uF9Dg*uttgOP}SWm3|Y;1Y+t~f3E5^qgpbEZpvrhBMg=&L*b zVs{0kRu}AcT1(`=UUd_Cw0>@P@$-8x{2NU+L3z9;JA0R9)^fUuyIN(Vkmh-MmOX&) zI0sj|>?p>NGkSB}cS0tZYJHpJq;t>phu7r=KOFS@VRm_EC>jwNUk{tbIO69(t#dgS z#F#hk^wsrrxhJn+g5|KM_B|eB#r8RLJXsc4r|C>n2cwYa3+7Ei?I99##D-Lz0wbYU zLh6aImhRo0QmR59)lJvQ3jjuvLLa{1^isis=hK33xO7x~x?3!)t8r?*dVH$@eR(*8 z9}=DJt9>kZjb|99#2fnU@nnhH6Il^I=&5d&rA@HYRio!wcs@il`{*dBFPMj59|$UY z@O<%=aFq?%Zx5?Eq?xqfn@Yh^c~D+J;n}dRTZ-Twckq%CaMy6gyLtDuO06lM_V|5h z*UlC8@9jIFqgBUgcKYM3_(>99b=|ki1F2Y8Pt>115m&CT{7in6Ul`4zA(}lFgYUja zZULD0izugw7=eim!DTe+jrwI)UqE;3+}vto568<&ykCN()A($_j(qX#LQPH(jB+uU zRMc6Q4_HI-&b&uW9lOUp5jBNm*$R3*$6;Wq7^-r>p@HoSfce-2n`W`#Tl0>O`p9!H zR^@`HDnvc1g!PZUKOIXYSCdq<4++b))1B?70(b?VZs9ZLs@A&Jejx=Z1Rnb&zLu_Y zwrqJM2xQAbp)(WI*ME3jQ%=t8C9`&+Kv;Z_#;q$YE3|p?1O;zvHas3eTelCR-(3|C z^H~wFD64ZCNO3H z0ecgA+0**Xrb8zNDqAHYWSh2uZT1<@(aCG0M&royjJKVFQ~EpD4tG9Td$7t8m;m3{ z7feIRITcMmeB`Fb$7iHCA}7UdeHAF{EI|#dn29Vqh#;h~mf|Men{CZMljU7ar5`v5 z-=4uP4biHY9VW^~wdZjv?u`ex#e^*=8+w^05|&pPAHLRkr^Gg`Y%W-WyUHl*H_$tm z5a8|xC4`;xB9)c= zW?N`5x_nqE6P&64pBfz5{6BN(d_q!MhIQ;rTtjE9CHIo11;gJm{19@NQ~hGewHTn9 z1L{<^gCip{zf3CNZcY;%I;871 zOL1H3hbaF#ezr)JK21JgAr&Wf5DY#B1B>BnV^F8->bO2vPC;CTcKtjzW$-%jzWUcG z*#!p|nW0J{>8u|G(2wJBKiH5z901#N3>k8HyIHYSl}858@A~A!#1FuF!sEvZw!XFDQat5QfEBH z1uAT2tf`GJ8J?%|{nme_vqGqGDwQrYFyQ}-vvVGp9rSlvId~}l%ZM`Y|NbMA_CH|R zey#t1Cyjt1mXW0AiU!Ik4ZMw_OIdXz|SW2^Nc>GEjQ4~ zf`4z1plj6IQ+o zKmP(G^m1ctKqIt^E(Y|mj#t#`!xcmipY-GzS4_KG$N~-EdkxDUtGEo-GTV;csRO$f z@pp4M0AzO}^LaodHCO=Fg8hfjVoRogO=SYcSC+VL8&F-f2Ju3}3wf=&p?@swvHee$ zu(PvSF(+||zabZ|=m-s3l!B~|FH%Rq4gG!oeKuIY5Om_fYx^r+N6T4Gy(5|K7ns3Q zaHQ8W*Ysh};-8(uGB6g#byQJ_igh3+C54}po}lm^1=dW1E+q5019|3NZV8qX|M3xV z@aAMQUfMoF;0oS}!XkUnQ!dF#%8%87y$k2jkYmqx9R~lnYhlfuCutfUZ<4~7;Tu+?s85NfIb{kDFXS8lwf_s<4Mv-~nP zwIRB-j+--J^L<-RR+4!%P<8Hcw;rpQB@5*t|4a7pIp~M}3j{0gHb5Lx`v15O?zt~UAz73F_#lPtc1+M*i39y3;j z-K>QS+lzhp4nb@T2xYp(pXo<43|G2UT-7*v<7On|RF4&2QdsYh1YFTQ_p4MM_*<)N z>ZWmt&)~h&o>g8vReV>~$zU9|8F#N6IR$fFRtwUB z2)O9k0o=S1rK)_eN4H-?;+^!YF45b|qSj`o+vzTHrtP*sIdFi~JoAvVm3OAMnW$_X zid($|X48`=!6Z78_=s4*8UAd)kH|tHz(5t@><2XRZ=Ug2~{f3D`Y+@ zG9VcNOiTwa>ez~CJ(|&uDZE0Avl+&CF}&J&@%oq(gr{qbky&j$TF>vKh*N)LMTgTU zs_pseXWmduptgsW6lDbdSL(>-XL@Q@Bea_#t2QAykDv2W$&%NoG|s?zdE%wYmC*#W z#1j?D{3HEVN8<;hD!3+Qp5~Yxjyh(W7{^qA(p2%oFq|stmQj3Rw&MFy#*zt7y(3+- zv&cx_v?DIJj=C6pO!t)6!KN?1QGePgvKAFtM-Y)QBfHKuecE=Jkz#hQ{PmO_skT#v zY<3NuA;=+=PNnkE#Axjc(+i#MMthF;=bxwV<~UiUzO5&<`g8@Nj!aRD?%1$Uv;-<| zr~9_k88x|XYUgG;ae|-*V62-v!KiA(Ib(!N_C4UW#vq@FV7mbPDQ?few8u47!WK)@ zOjuLrrRSY?FyxG*>e~yCV^TK~Nk6i9nddlUt*BviGaPg&5fF(;1ID_)j9e03MEtFc zBy2A&RVi<|e0!@8ob5-&N05omxB+q)YNE#;o0u(Vu)FRuiL#5m60n4I%;d%`c zwX$s|iON>*7bNIaGL*OBn%Sr3Ya$3gxI8aG_ovMucvO_oEucHb>L_2^qfW!5)V%&N zUpl^+Ly`uK*)sVwLEGwGgsQcyU14ZcF6V?%i*y33#81`>ru~EUx|VlABIIy$UOI0!^D9CnRXkS_b3N$#a<1l!7gJYj3`1@U(Js1XeBJqs z&jp4X!k+R_R%-F}JAjimf3seI{Uk^2!WtNFD5l=Aam5Cd<1Jp)dYBZzMqDS!7|SmJxO;r+ViOgQ`|Qrk%(woPKDk%$sGO#?TmO18=K#wEnmeR^a_ViH#310Tc;t9%}Aqb&)(>S`BCMNKy!jk5-F0> zFlJ)i{ZoU(t-?U#`sC5|q0_Y0Xo1bf8HSLn9nwZso=&5ViaXx?zHE zmproeW171q`ROH#@t>hw~obG3`a1LW8lou&rfJR9=mR`lmk?_g7mYRkQOK&64a9X3UUu zRprx8SscU>UzM*ClE>*FtVi$Sic6Xp)&$FR#|(*lUYmv1+Z)H{w&~68?I`0?mdH;Zd2qG1JRU zaddfqEo>4DDx}onOObs#ipzjRjoU_R&R?lnB(k#?Mrz8)!2`tM~iu1Nrlft6Fm0tEE0rF;vGUMpOLP2hl;_g zNQcogWTHqM>Gq2{8;Q&uWe3mTU3U{Wfv(T5HB@nprD{x91jD&cp{x3n$jtuN3PY{g zRhRkl0w-pB3-H3E-KIBS`5luvXmfdO$d_Rugv<7@O~m|jkICJ0W()We+A6~-CmEu6 zTCs$h^0^lCtZsPB|9!3B_~wTGbF+k5(ND>i=Ft|WPd%C!_4aW!w72|)3*Xjo)G8@N z=fOX;rG>S2;3>Yedt#&wFwz*2ko^sBg-Nt_xqf@LzkWw?T&?ctsjsr7w;GAfO$|>g z!2kOyPr)7t8NhBFn%5^S+O>An=ile=_a$vwODJFKdJRETcRS(3rRV4$(3Uc8q*6-2 zB`~fqPgl*kkwf0LG3`Q@+HIL*+kCS}rtX(pr^Pf{v&id2^vG^emJ-#4mnK1_1#uLWotz4=!p3-;*4p8PdU zSgyFLAjEDA*ZIXLG1nzqH@!pirW=R;ea2d^u`sv?4_<7MD3qQP$-39iJbnNaJ=5idVhN7BGM8C9;zMkmJ%Xyt z!fG3em*vt6dv4zGFZz9UnowrTzmXZbw4W! zw0p25BngUt+C4jpWnO_wmOt$t(PS5;7gD1VcCK&qn7zsfw}6XFrCwN7`6x`1)#AL2 zalDaZ=q0BlGZ{J(uW6YtYEN?~jDyV8qg*M`!!Jfify}E|{9!OENzXU8ZgKZr<6Fln zG`qb#72iWe62CuY?U?S{*uCl;dr1mQ^R66XQ5N4y`L)3vL5FCRdQ@5MQzkp|Y%r-D zOa3BwNR`1S3qU%9-77<{CZP4Z@~Jz-fU}oV6EAU{TG2Z1j#FBj-1L>gln;^8&>GYi zw|v-gII#$cl|;mGx7VHA^qKZ>V2sh;s_BLXB_#zvVPb#)IF>I(9Jysa!=<`4>Xftn zc0H-3QF*ZA)0b}1g`m7VoHj;Whn`RN7Gl*M6LH~!Ou!n~un1%!NbM?Zup@KV#hie7 z`++NNhQ-5cw!b|emDtyw;t}@JxS0|yHjb?xyLeZfm|Sa{_`AZ=A3njXvi$3s_EZ)lk0)B!78xyVeHn z4JgcDV&7V)s*Y{r%wtu(U6$12JO4@xPS)}xV2wEJW-Vq67pYsjO#fcbRnSf4+3SAZ ztSX(%rKi2iEB^CI@Cf6tU>VGZ5&pqAS;Hq@dNv!eQ$EtPDs`guEt~IB-!|sHx~K8l z=a%eSdk3|sY~jrX9;3eCe&2;S=4UHBj)2HZ>BDnp$awTx<(sSLe3K{c?wxCj6*DuO z>?&c6T}@2%IILK;svIj%#)c!cmzcoE;q2S+Ji;dz(m-CFlnU+g;td!qX#C^#$f*c# zlT1+*npIP??6L!YqBig6g4pJ(aqN4x&kB6fE9A~e#*0<%9WF26U82-*15O$2*R4)R zUV!gQgX-U6D#Sid&~qdglTo-Wku7rgge-uN6SLsFns)2OD+T!!D@O_L#>Fv7ON7iK z4n;R-3bS2)BnVpOvebSh&r-iOpq~*cKV#)*ekcdMP;|4Z z;DZzb-MR}Y7y*B!kJqXJvF(4uoBD4C``%7J$D2~lpOi8A6eOx~+sVYp%4oF%F}{CI z6D(!dW;#>XHvc)ns^FHK3u%qD)fvno$v+6T7=qlWpB6}1ZC$AIWA0vEE*?BKOsPh? zAU$cw&RB=mIf2J2`t)QYoKA~X!z1cB2 zb&b6P{c=hnarzJ%Vj|kc+%xkk(KpqJrt#=DvMk*3H`UaEM7ugMSsx1D%VcZP07AWH zt{GhdzdIuH?R(_9`d0#U9GXakNc8-)tkV_%ZptwD+F-Nlk-?CMGpe$_t<2=mI9`;Q zc~hiyS5!o!kd6IQxEvl`Pxbx_y)j|B`}{dL`CWC&z07JF7$v3V8;r36{G8D?sa~6! zUcLC2@~rApa56?(E+#AS8MR!WIZee}-Q=0%nB?y7o9=AvsT7~48WD3B_8{bkkKZ@| zE`9nmVLk>Dn+4L0uBB?zdFHvQ_D`cP*Z3h7K@j&TUeiLrvO6e?m>^ zFLIcp99IO0eQZIf2}lgsAin~dJ?~bgOv-AkTo>(hUIzwYZpGI4d~ooDutZJ0YBg;U z4lH!glBi(9O3}jou=n`C1)Cl%|lVSyhP!$*W!w#GeQm1FIqc7R(ZC z5KQ7}ya7$*8@m>25CX3yNq%H@9h){0951`NVxvMY_U;oVpyjUnKjmuhr*ne|I5=vk z;VN};30xf{M{S#Uj;t>Fo0CVL%OYDlgrAR%dBY+9e)UhpX8Yq zIwi~OB`nDE(Uz`a?-9O^n}|?haTX zEVwC_$lXi!Bl3^78jKBb37w&(TZ>GZl5OI0Y4h85;hMhw<0I=gDZegT#@;L%BkKg`3;sJnvS>>-TrE^Y18#vhm<#(ItA4l)! zb}0@SUualp3640ox+F<;(vkn@@>m7uctvjOW=#FDF-qE~H${B+!XI+A>dG$GR{FDu z@28l}7_hxp%G|;N;Y$RFegbuHnZ=%D_6P=rXt3QWmi3Q?`Wta$<-diY7P|%pno0Fo zm{s#XfvrNy)|GRY$iSBJV@&;7HH${clS@R@Ea#ds80G|)z@|M74iFCY`IoG{^{HyK z0oTl2rflrJbDn8HK};tg00{WLKVcKYH|;7>Sa)PT2DUEM94^L#)wAgpRQ$Fn!Y8`} zJF9wUnv0+gZ<_2??wzDr51e_|%1H@<4Ni#U)8*vOv|TeZv#|QpJ;)BY_mZM#hduEb z|D4K_DX@H)VYx5u16T>%F>~BCBmJX7>jBsg3#{9Dsa!{g0>^2)t0M|*V6A&jZZ4_-A?7+0uU0tghO56Hy7?2knG(-OQFl6%jAr!SaDDg%Onb z#LUTti@W7)k<}julZ+56Z@PwQ2vu%1v3q=oOlD6$1UgNP!2Cy(z-%wf{_=Dk6LIOd zGd!AgX9v!{>h{k%ALj>M^6{PDJKY9_k9{R`@)KsZiL}YaI3i6Nfk2QZDuo%27WI6f>2A0tyOvNy< zlw#hK`Th#QhnQnj&noSfTK$pA$sfd$kNWRYSsie|+V@G4E+1CRcx!6lswj zRBk>Vc*u*N9dPqiqM3oRREg$W%M1>BX>Kw3uuy98X0u3xpyb2u&W)~(^^Nsr7i9JJ z>U+P=(5cul5ew%!)amI#=xB|b!|{@o*oq*@Ry3>DJfM_(r0lssG4oN&rj|#c;cSlP z^i|(q`mDcBvd>{@FRKqJ50-Vzg*mVjxuuJ5hO$Q(B|FW;f1cJ!o_yG8I$zvM9Y`k? z@_m&J7yH*m_bAz%3XAXR)vMn&rm7cK@*v$YdR}`&2OVm4H*IRRpVoQiusp>|0KCo+ zsy~NW$_70H!z%c4$fu;FVviuf$j zX7y)FiDA>?^-F?XleD-$U3O`0KYx^WxS8{hzDyiMxJo zji3J-eD~XjNuj^k@Z+Dbuz2qN_sjghv&i4$|NrDNe{W{7fvfAmUjhnvV2L&>l5P4u z0qM^2@gd4?V-m*iv2S^mkT4q1`f)L{g>fmg6n0e`)@TRm;4SQ$@O_V#t(Q?>z(v3 zAFPDJn@Q>e80wq_t-H!A6cY97KlY>h?B&^;QHadHAU!;4&FDv%x&TAo?q!JgR-V0= zP5nWm!osl48Bn90yMnH4$Y-jruTC;QL#)x(p018T40B)UojzsvI~Gl8vhh$0Mrl7e z6ku!M&_c&ko2YE4EZWx)qBRrG7MJe~)mvqGq}#7x4y-Vro7}Q`W=87e^q;wIz>xD4 zGY3!4Z$KSc+{wc_rDP*T8^&c%$L!A9?Io4mSXXudkMK=kpVI)dmu;6HqO_nHvk!95 z`4F#Cy|eg%`4bWx78NB0s*klCBtkn|d}DWnh=L{8ghah`#1388M1^m9h)!Sjz%!W8 z+w>3f!qb*|MP|Xu&ZqqZ=0@UH_lOAxC?(A!0;g#Es%J+*vz51 znSRM&VtN>zC{Hw)dW2O_aZ8U~OYQq+@`M}Fq#Y1M>DJ&YpnfUIb>9B~37jH7e+E8( zoH^aEIE-8zs487w@1QBQ3guUa5h4(+$r*Vja3-w3g0aBTdx{Z^jD6-6XT* zIO@vmy5t5L)q%~-SfUd}0%!+}Cks~kaMgll%nAK^Vqap_5N&ezk*ynZ5?Q%uQ+yo1 zN;mNiVnWKmRStGfgNu@`BDD3u0SKOX7fTGh<0^8@*m4=D?FyNZAyuT2mR!VFqq zvG8NtcFBqCJqFJ;30}4K3k6Rv?)~XHh7B9hN*0TKOR*@@eXYcg3z&N?r=ITGp>Kqo zzm3jq^G`o~s38CC;F?6sNp?=n12ZVDIg+HfYn@6f!6cJ@9^!7LAnIs52`F{*Itn=# z*O7N->N`ZApm!Ex<_FldZ_FIWuP$WTtJn`ooj$5_TR3#xTCRZ25C%8^tkje>1}(_M zxmHN4uaergkkFb6Z96AVb9jgA?&0(dsx?WM7(J<8$lSGDX-oF}eX@>%$XNdJxT|{K zcuDs5r%KmKj<^%ikgN}5fN@^dbggmv%s}y-=@)D!^mSTeE*`6y<-?ipD*aeKVdqBg&*bCoL#!}lXt z#m4wmW5~ZT)Kv=&^KIzKQ`=aKJ-R+|>dDd)N@+7?v*0?$eUc~-G=B@E;}|r&7&`A@ zii^-O?DJ+AV!}*VDiGss^e`_IyB~`{9R!OY1!a;Lw~7-jvy=Gr5lHA1F!7)bJNn&V z2YTl0WCorQ+h1NWbd$eYG{|IR*Vl?@f1v5qx4Oj;?J`lAysYVlk)GV!4!e^Rjb>^o zS)Dwnt!0Wf8YuOeYo|nEdTobPPrWt58O4dq!WklMCB!{!tRVFRWKL+Zt$JTSw zqLdvyXJfU)>DozTJTp1#$GjfwerRAc@?q4D z`)QTEK$lu_Blq;g3ok1+u}w!gMhM>ry6P5X`1%l%)@l=2hgoH=AY%`YhIcThYh0!K zTf?e#$q4nL>lYi!R4228saER`UV*)X(z=MMKW+UM{oBo@7;{Z{Q*qQ3LV72mh_1E~k z?4wahP#4nV*SxXrqVy~=eK{a(ND-IlwqS^xcZPJKWo=l_4=4;ec{z{lki@nSq?h4c- zMOqIbUytGa_oeX+22!V|g9~lb{4bJRnC`2f6Du6MboFk}m!QUL!11WnN5b0JUw->Z zI?(f}Vs<=!Lm{9?-AcfD%WtYUN7J^W4zj)4v7X3OgFp*KHHd7oW~jmaHjtzoVJGaE zK&vFSZ)KM@VpjutYBv`o5CPpgZ2Ou_b6q=V*{C%Gud6CrT2~3>@VE}^qm-glt#V>w z)@)Qy3VE+qs7~TUC(xX3eL!ftoYZ9M04ue$nuW^)f@KYD9_6EglsH_PPAZe-(95CK z?=y2;vYY5Q>}5WD*6%7GjfoP-Vs6lO$Eip!s;=zD(Viy%BDvC+NshHgR~ws2HB6kpwB%mLDlU)#5>W7lxIaBG4uZ*{udkZ5#QW)4jTN>>&>>niOC0 zvB=-n-!3%Q*9y~Y3X0K@P?)$gv1n^JQWvUx%T_p(<`bz-z?C;K^~tSbB3_~!WQo?q z9z#->76Nf>n%~^s>3*GfG+l{;0;}_9JTVc;t!m(){1$C7&n~81$Ry%k)K^lGKv+l2 zaky$PJg*Uw2s49w%$JkuhC)vD?@@aS&Ih^>HLDy(!;Vyrkw)LFeAOc11gp8BSDmiL zRg-(Fts;+nU0SRVYgch3>`W)nlaG)KUlG|ETF_LA&S-okjaN&vh06^9H0HKB$t#n) z5Ed0L+Wvmrt(H5}Dmfx&1%p$z8NFpDT9e6|4-u?o0b8)6UQxhRr@JS+IKIBZBYsdI z(UkgfdT(_fxjwBQNPHu8{H{cKAsP(@gp@wun_M#sD$ak*LlvIsMH5sU{njwX_RC$| ztO_DYG=F*iBenADNp5F=vOvA^_WIUAkZq-={-moE!og+8z4I#z(35`RkWUt{hgj>x zvuLi*P#wcm;|er{Z;e|(q8z67P;sg`=(lU;WA<{JU-BPnYiq%WsnMd-#j-Ja5!Ft( zQ#td`*>enDLho(wDqCT5<^_&PdCQ3q-(qUVimBbHPu^Njz3%dFK2<*mml$Gb~eOEDG$=_Sw`&6>31aT<^|+tHTh9IE#`}eMpRfasG*~y#RVzUCa5+SdNoeub*KsE zU@3U$Mdc&ItFt|I4A$}Pbf=8LHbU@X2ONp*=0U2R0Y-gl%W}#~pGngl6Q8~kwdzDo{`mwhalAZ1hiUdDO4hL87DgK!juKU)$314Ms$pfx33454M$ z4g4uw)wF4l3u0$^+TFxc4;a)kieG1W->MZ%t3F*px2Vt@)>fLX1iKb_So8m9gwyK#4D<}t@KZQVb>$4R<{B~Xlx|r`HQs!K;?^7yP?@k=2%d!r zSFK!I2jS}e&Ov)CW@dc8)+8I*oUihs9N*$~#DTmWp#h8I*%0GanY@P*JpjU;@CCb@ z@i#KodCk^c){UMCZz2=3ci~@Wl{sFra~E~)KICIpsqH|qc1+g>JYP=S2s3pn>@!>X zhS+6CS$jufUi-*^}IN3lBi1N<~YadTjp(5L#k3I1;^ zq-tO*5Gfo_&-_vv_>}W~w-TtQaR=a^YuZsWqEr>$)wQ^r;Gs@a`vz4|Xt`DE?Vm+I z-#`$&kqVxtW2Pk6{I#P)31hY1sFCim4~3yN(+8@aM=}K%>AB6;e)VsF`}V7i3C;x} zw$`V0%PaO|>iofMfq91GE`Y5F-TnOCsxa?l4CMCEQd0JKfyv7ofJN25C(q~Z?woOB z+i4vw<2!!&Tl8kUtB&1Kh2bWiE+T#f55<~Zmp$ERKk2i~nfy>QJEVxk zQkT_+U!16E%U9LnU1RSZyeUi*m~E_84p|?o7`C8HcXsMXlr*<_Eac36gDiV>b@=X>4dE`sKVRdxSlnBycL62*=c#~ z+IO!|un^-9$-*2IkM>pCSUX;sTHCu@&2TcYmQ*I-_}C5t%-B!$blk;bv%e##U%bc0 z$DS!XSn#ftp>Dz}JN>C!ojX;$_kQ20@PmmFmf-JIVk6Q5(i3Hmn3MUsT%-HE3*I%~ z#VX7gquwAcLoXidSJb?U7a*D0m6+u2Zf&HQ?^Qdv=B&=`W@*B`dY&#Y^lx-s7RPRyU^)eG&U1-)$s~G5w)vlX@9*|9W5R_1nZb z2+NGcbzo2lZ|5&9hsor{!OM~*D4{9_c_+boWtmE2t(O$;@(Z+xvbRD`9D)-wTvMjT zGYf&io2ly_78nm}cQ>gq@CLx^P=&X~@#J#R{Yf`u;kjWX_rhum%;~AVOX=GdXw-jzH2>1n;I0-0xo3@S8CaD~wb9 z)v!fg=ZCh8UAoq~!fqOT?{g3Wl=ZAp5flL)gEf?i9||PT2h@b4QMzU4@M(T(O$*>; zIVC*d5M1J^3nP)Yl#}4+={Q1^l@uJ;SyuIB@~VO&o$f=V8Ps`woZo%-HOz6FO}p}w z0razT2xx$^gumX`z-U` ziOu8R#5>?zy^|!+$HFpabuuNTYPnNalg^eS-<%Z6902O~4HIgPE_zfXESqsvQZD_r zjOWs6b_LJG{As~LagJd0ma)l74xWMM>PZzJ$V>SI_KUt7sJe4aM;N-hbfF6GA6fPp zY40y(a#!+i(G~drMRonHklw}moj>)bI-GxplCtJSH+v;0Og1A$(J=d*>g40^4BJ~x zL@_ck-Q#5z3rl@zi2mkk+qu6)6U6}0?nEI{u-7Pf4Z~jX%k^;L6A&QFQ-g%4yeb`(KW)Ghy`GkqhEQ~Y zfEJbT{-~iHXmz!gjp$MUXcs`uT0{G{bgeHsluNM045{Iy!E-HV#BF|dWTW}Zh0}MI zsUleF0ZvidQoycMG3YGWcLDSL7k-fjpB2h1uoEs!$%8lFbdvgDr}J<*lvAQE-p|AJ zI8V|FJK^h84`_HG6!W;dH^B(Qg@!jch`4TYh}Koj-=aCn?28r_yE#^Z<|c0o+6vvl z4EYA`@)+mh=9J3})rhW#74;D>w{FFqiw(c@?*`9Y6)a)3mfynqO1kO~JnekVMYhq7 zSP4}gr|k;fifq5Ur(SqCp%0D6TCwZF+z0rBUnkVP510Gb#{Qx4|95&kxc>jwHU3{4 z_n%7qz<;uxEN%>Sb!@{0dGgis?OcebY#pHAiZbIh?N0Cq1>6`Jc$2*_60x$~O4E~t zNS|@2+pNRvn8DOJ`1xJGBxH4g0~Av=ySMuFJ;9;l>G2`I%Z7G>hzD=UX8q(3`Vh;} z`QAQ6h`K2YaA|3=zlO>Rdv7^iBXDNJFhuJFI=tJUuZlT5RxJOFJnk4kH`nX#y{|NF zTCtalKNBSj74>|m`;e5byruhtC^^l;n4_?e;xo)xr`*H>fs4(@WxB-Rh)^#9LJ>VS z)ry`bJIppT`Gn;Z{BGw+t_SVag~IBM4rELTECFz!JN8=o@?qypH^05TUA@-bDPOaU zNwrX$4D@>2vJO*me;P_frHQd0l`4puU%*DS@B`7U5en-}n)sn2WJ+Co1FRoiL{##F zBQQ|J$=I;pyc0$tIxsfJQZIOrR=oIWcC4BPD&D)~OUm8>3R&SwHHSWsdp5YJw{@cqs8(G0|7sak3P((~OpA=62L! zQjs;kK*3Vba>KD&KMEm2q6q}WNkjzpMdj2vdrs%dXZPe3cTlbCC9C1>pr^3Hy!GFNJ0U^8qJm6YJP?sL``qpN^13Fk1P$Kj!-*Ep9 zN_7RVI}$R;JQl)B*dN!@xUfCrag~>OTFCP1Xpsn}#}kyxPg5gc1RUTVKbz}*Kas#Y z0*~GF=XoMNmMGEtZN1pVBt3uonKnGyg;5G=;d)>XzH3v)rX5o8*s_Wkpd$7*3yc6^ zi696(sfJ1~j}PAWB>UzjAf4oPYTAH$x zh7ftVNJpR^q{CxFEzF`_gT-;$f6Y0SU2{^E@7N?{GVE^1RXC#zSG5Dzxu^Ga*k+^P zyNnB6`Z_X@Fsw`@0=i<@P7ar=>r8~Cz_Hm&!zo`ra%2NcuhM11ofHlCX$t4_7fDwq zmS7OxF@`%%7?{`O`TLBvYd+An=j$~&FMqc?B>Jw@M4I?wLl$*VrCu1fo*x*N+N15- zZQB3t?-3^06pP+0MKwn9&z%>SK?axl0f%dhY-EmlOtRejNtUvR+0P!X7;0n-wQMa`Dyg35xJqw#0Oa|i7mJW|NHcFjS}z$Gnz?)n}dBnv=Z zD5;^Z*ZgLdXXYk)b+7o=Dn||W?QA7@qkjKm$4>2(_C!xu#}-oNQ=!*{;arcAa)=hQ zO$~P==&zOPwG7_E2%4`0N97rHO}4~5Xn6IInZYCXLg^9ep$vanSW-4-`Bi`d(|g^) z11kXdRT8IeL-+}w44(y+vmnN~QCnR__AKR2(Dm|yiH=UEV6xlQ+lww#r#_dkno4%XmCe6?pNa&PfNc@jqlyzWx8ur<2+3x{r{p)4T$6ka50-X$aV)?e4- z)5RENLg{ZWV(utjZ>ZX#xiZ1cO)Bw8jhbx!{TsWfuUy#b$74U$Y5IO$Qr=)o!zT@C zp6{NuV9*QG>E#p+SxH@y7Pu#=zh=?FV`RUVI?ZvPG+ezx76kxKmv4!kO$0uM6Id+X zH5hVKbQBFtQRe_g?e5mxcQ+R`b?4*dC9U1ygd!yBC#~_x$+MuSN&}@%zS##;Uz{a* zP)Iop7%A1>SxCVxU-+aJM*_JJSXe<4MMDleG4ojb$Y?IJS^7D{HQvM;E%I-~6N0~r zX#&JCNUBe&+d{-A(_pMq3#WfFWIjJdr1tE|L?FS9@}OpP$nQXssM^#y^<-O2C!2Rf z9(9X@GQRr-bt0=%sN$aD-+>^m!Zd|&yk5?3f94B!Md(wv?qy#Rt$Z%?eH|FnQUhOe zFI{O6qhQXY=PtOe9RDVjz6%C01d^n;0`H(^mb`y@`fL-n_i$u$G<087O9^yeaaRUxA~>NzdwZsjhdbho?J;pX3Gynwh#AEUKzaTU73a@S+^o@uV(5u z^wIXzs*2!paFxmKz9`mIj+9#Woo(SgCC)hZT-4?{{=T)iWzQ8Gf4m(yc=9>($$)-v=K)5H}@0biNw8m z1_e$I{xsEG@@4LYZ1u%)c6?mi_OYhFI+*FG*f1rgf}GSH6C7uq3_vzQtWQ>qJ}OjW8$S_H{O^_mA^s zcb!#{>>TXSEE%rS+c~g?71j*Y)|j zeP`KDBr`?d8rKXX`R76`k3VAVSmB@lCvV4U_djkI0hufDJ?u8xn-Egg6JM&0+l;w( TP+&+vi(lNO&h&sklK$%y&`8co6Oh&Hcr+c6NzIvcN zrNuS=_~Ce-91EvoCkHN>NE{T^uJ-z#Gjye(KeeFA#(Kb`+{-v9IP%5qtI2ude4_5j zVtBi&*S(#e9(1*P)su89?F$~k=v>%cjwp7-B}Bx8FNL4Kw?lwXKnJ7L!+>P|I(rJUbgJ%q%f!6LqL^boMp?ho1GU4Nz#FG~yo z7r8f9k?6YLGYK!{lnRFfkd;p`W|ueoYT696*axQ$l4W(a6S=o7QXm3HhF)1~;7EvN z&tRm|jPQTRr%bqh^kK~sXw;TWBa>vF}5pZ0Lx`V=50X|uFxvDroyo^q@t3Y z*vFa?m(FD8bCW9=8&5$=X~9EqyOD~^wFy3=t#j{f+yolYq$us9tzFwtyVm9NW5GQUj03Nzl4)-W-foLP|^ zFmXtr96@Upnm%D8BHa*{Z&e#+U6?3A;jtYZi_nm*vYR2dSa#E)xwI}-Ep>+FQD;~X zpBLJo6dLQ)a`Q)!H$!nos6vgj3{1q6{9GHokbG5c)IRV^COo1x6XsWYSrgAWB+;HV zEsp%I0rL2BgcCKo>a*Ep#^jY5>?q1WvZgLmBk#J(;JQI`7-D@tuXj)JvJ6NuDIcXSj|u#83s=W z6yBDR?(zZE`*9)pm}Vm7xDBP_zKuKVqww994L$ho-;kYOAU_xKpfp4G8a>f3kIng; z>Jm_k6tGTr$P03|V>duV)58TaW<>g}H$TADDOsB`s$Dp7zb>4*Ct-b74TE85MQ#UX zutZ2sgY~ljXzVgr0m(A(W^h+BJWz6raA_VWh2FB^=uifMw!ZbB9uk`FV9IrmuiL%#0M^KG*SrwkMI{+_;V6&OSLe+`Pfh#auJ?R~laY z4$swvw%05BZeI))i^y4cCC)#a+NWWm%9{jy*L(-*em=P2yBYe>QtR|c3B-TIC=bYN z^e&B~eN?e?fD?Emso82#&g-&s(T6Ho>-6s_m@qGlUPGftZF~L;)57F6+ixd*Tz}O@ zv|E;4(P}n=s#!>&#pycv+^#&{&tpb1Uf1Y_`D_upvV3_$hb8C^5#ngC-TDzX5-_=HObCBgcvPg&iqt;DxtscUM?R$=?0`>Lmc#0Z zF?~i*Q1#w>yL?^ODjWAm&NcP2($z~HTK-ZnU-sRKU@G->e>j#fCsg@1AXop8A%m4a zUn7Uo>Sw&~KWJPp6;cf2?=;$6>E(s{IP6L)xahC^y;1DH@w?cc4-WjMo!PxwE!(X& zq`Dm!Xk(9JPEx2To2;`2;`)AGA*(b%dT57%N~^EQyw5P&89a!m{mx(xDiS_UTEzcW zc`#;s;cz}bqK9qRC+V_3lFQj8O}^wk{a5B7W;3{>f6ex7D>fQbec=dC(v{M77xanQ zrzbIO{DlRk`dEAPE8tOt{N^WO+NbCWJ__&Oevm4%hZ_3A+rqIPmn=7+oGYkL<>T$E z!$q*Lx;fHav^XOv&u1LlKV3+F7lWE%_j{!5Q>R};?;@f1idzJ}pyl`}l~=Sj=Br%I z=0o3I?8?e{EP=5#AC)$cJay>MuY1LibnM zdx6uS60TRO>Ld8)6Zd*bYaNYe`OgS$+3g&nwhtG@EO%7J`E`!_rcG`obGOK0Kj>_} zp-=})yuVf!mI9jrn9$v>HMs|CYL$1p*#VN$O5f>tn}q#+elmHl^^e*recYK0HG*>W z`5A>@u662|3`3V?QB;M7>vpw-T=sipAu_*MZw5FJd|Kj5TE_|kfbmj|$k=U!u9#_VT18`UFtPZaRBeK}RyOElksUYSw^6 z)ou|YJ6F75u2y6Wo=zp}ULg4SY03LuW^gx)#D5Sq_@yySO3lzv&K{)uw4V3RJ4^78 zt#~0!@OHkr?AfTMBqgkEqHET9qB6LBt81+#1W)p*YBF`MJr2PT16tB;agqiCF2Y%+ ze_W#eeai8c=>@C(8Z(oTjvtZ~G%}!ZRt??h};Kmu`*A z8ZPN*KU18r$>|Fkzqc;2{l4FEQPVwenJYp41V2d^PqY(AI)qKQ*cUZ<)MV{<`jr)0 zoBY%MBh*`@To>be_|(REvkqm%5IlI}sreL3w+2u#L;1#G7u!|y8O(bU#YgPRn>0(9 zbLOEA(6E1(0d&`1@7jJtb7aMl&gzDJcB;Q%T_2QPyH>0)EhT7s!6$Srdpa0|z}6m} z*1cqN(X^*^kwz7|&I9Rjty>L~!^xZ|Ur@NH4A*Nhy~@O6^h2TrvrOZQlc-I*rvVa} zl3nZnOCEHz7={fFZUHGTKD)T|Qhd8LOLM+WJx!rPhLo|=pwF1% zmDFnM3y^Uu8P*OjrNJ(0L$0|WPJB_Ynhvt=yShJ^U$S(nPYgbv%fc4bVyxaU7`!XL zm=jl34??c3x*)=KYMa<#(~+c6i7D95Rrdx#8-@aJrKlsP*L&&Wv<{r#b6i5P zRFAyJSfawdk3pX*6QLkYMn*Mq%yxki)TLk^s_w&LB=i$7hD(pUMef?o@Kywj7?bUL zL5q{UL)h%rgSw(_EjZMu$)!wa0Rbw9+N69iZh(z7T!@L z{g)vyc)Tf3$HM-vVeEHHTbnvj~a z4Nyb}*l= zTx>QvRX4f96Kkvih@%k5BID=S-|YDz z){$Y^_bX2Xt6+CwAFq6>b8bU7n|bq@s)dBCM`oGG-9cUAU;2>Gp!Pil{bn0TJsj}7 zr-n)k_U|8hT+Bk-K4pZa$Hk7h-7kMoLsQg09No$gIaRvxQYh%lv~@Xyk>^(j^Xg>< zufdyuj+?m*elNV0AM6UBrIKLk47Y3R?5Xa%o0c={cGjn#o6(Zrw=bw#pYC9*;*0#d zXh7VP`zEN zN9u#Gf*>%@Y0yde!o$wd32=6%Ds0};q_*SpY1}BaPf2d<7S_JNK*!Xla)iKB$b*tu zGz;%sSU_;4;6?N|zYdXcmldL7Q6b_>JDUNm!+0Ca{s1W*Jabk(>l3${pGf?aK7a?} z)f~Koa3lQbv9j(a$~c9F2pPwHVwR;3-M%A!hkS8) z+6YZUhRo2rm@Ft&fiXU`GoQ8@M`j}u*A~d%{3R)?+%l)Esb5Sw7`jhf+ifr%McfKj zD#oqWQcoTn&J!j^l{&Uj=l)p%(uDk?X>}qU0gF?@6>_->o?-@!VuGmDJZtR&zxv1# z)>E@ouxH~D5q_w)gK(33bos=B=m~`^stZ=-k;y?YdS%!pULbHYd}dwD=wS_aO1{#c zqx0e+Su;=X3S3N;(tsU`C*|5Fm?AfITK7>h))&4AP5<^w`I2Kj4-m6mVqU9ZCwq%J zs`}g}gp5l7OSv}ei74AmFHNrw@ut}esk$m7%O-Ix*DGpt+WM~%nQUbp+VRa+5Rs%J>6uS7#v88J=gURlg$ zGuvsoa^X_x3YR2IAl|DFJ53+|9EFa%}z4UZJs@_{5 z%q=Xk`w5h)E}}MyUx9DR4X)SCzTq(Nh6yw9Nf3%|$p)r)&Q?-C5w(zF2K~JPmP&o| z>z{N3eJlE)N*lVTynw3t0ko7hja6A2rgi?^kz@u1#bUf3>TSdst_pTnhadxpaT!)` zxg7y=H_f!nS75C+WNRSs2cjq^kBiW^@54Jl)8;_u7fy9r)1B24-r*PV*2^0XT`Fbv zI5zhJi-gEW4^oo{hN)M5`FdD9)R4n24=VaD`V?;5ZQPVLNEgf!mh{L;(vMc^!Z`&B zcGpxi^twirNzQp1)zo?0R8epT{QM;o=9Thep`;f-S*7sI2M%hVt+$;Tm2Q5)U8E|T z;0m8BH!QdkeKq5S9}!TZ(f4bR#s#<;5;X?UK+@RJ`#N$tqu&PvljIa0Dviz)Wm4(o zF;;>VP7r*vd8*m=?|CN|$VG$$UakomahCQxL?WkvrtvQ5f8jhqD@P#C|& z5U)fJWd+)ibGSnEJeudecp+|eq4#mB`I&u%nZgc#eZm@ekuN%g)RD~*XZlCmfwGHz zB|xX1re3&@@>R&O@V|UcB$3rEg z#!>^~0bG*J^J;G29LA@e?XM^ijdK1%kugoY%I3i`4+m16OpYcozj)Q&{C6U*s{4~U zDyXPAo!PtNP0U?f%YM@b+`A(k`jw)c$NL!pS)N*VGeZx(h0ruJ(et5Cg7`811G4)U z&e*7}Ta&&urN|k2Pj9SRNa!0d5x?C}oZEd|F8zKo>zM4 zV0kuVutlrb+TgMF&_Zt*Ur0Kr%3H3Mas>#N)lIKd9CXMp%MbVG>)BAN)zeyPS<0JE zE)O;A4bq4tzS7pAmY2Xf$&TPs_!=sP_Q82w1>X3PGj3*X-+UDwb0=XZo$Oopi_fX9 zJYu_bMg62Nh@W$s?9IpPuhrz+z6Bl+zlfSi#rp?wB#s5VKewXz0V$g_sZh6p@uJ5m`LXtyTm9}Kp}(kc^&neSI1H_L4fpNsLZxClni1B1ut*)X49 z&aE=7)K+yUqsB{FaM6;0V>~2qXB%{icZW90>D-iA;uN9k4hiUhO^LHzYGQP#Phs;b zzX0`8veCpdpO*vPX~OVdB|n?0Ys{WFy>?i8o@T@JO`CD4)*OUAd{9!itfmUsU_HuQd1_g#PX~M5EWap71G$yKu#2|^I3;58oY~vc9;cOS zoo?aPB?h~{R*YScR9bvQev?sCk!?S1%2~o+Exsi}2Gr(;k)8OXE*-JUM}}(%Zy^Sl z9$SCXxw7zg6?kl9QOa58!PYXn^-QoHp(>AjWN!ujNX|cJ_o*~PW`Ej#UCFo@s*JOT z63-nj6I_a0Be2Y!7w)eo!47)3x48v= zVKdg&O=kT2)7nDdV*@7{r-y$?g1nt~>=MSK>rOkygv0pz+TAj?LvmzWA%#JmC7a73XTIi!B&WeiX6UqdXb~2AspG-;@vh}b4 zp~fG#MzO!bU?`E_obw)kGt6AxS*@LeZAJA9G$Y#b?-mWx`@lC$V68GT`$$fWr==Pi zAC-I5-&@$pG%cxs6FWN{q(KImoDLcLbY}G%)%(|hnYpS0+h20o$K2jzT_NRJ!zrCR ze|C2n%nN_-U9sAR<+qO?<;On605c>!I6)Bhi{`SMlBUau^gs&LWOEs(4CHlzT`K#c z`t|$vFYSD8V^SK#ZMZ0SrNyPMzfHz$FR@=U{BCy_>lc=UeHmR`FG!t<9qjedb=ms~6jx`VwgD(|iith?5!z^teu za)0HJMK@pEte_nH9nj6Rz+yDzf3}<;J=u1I&Pom!=vLiu9?>jJJrw(;=|azU4RHKM zv$<0%AG-cc&;^^@;hxROWi{tm+S$%1$pO@@!xS#j>1*j5Pi6BokEq@4$ZP4|G|sa` zy{@DNcq9qk%D;DEL@^{H0{BX)9x{Y{F#sR-WdwV0itI+3g6&4fjXIkzSPZp;DY1bb z%2V^c(Uo9fu=(p2TRAeBImrOhCuDaC#x>rsZ4=kGp^^@;>P@4e?Tn8I(RY#1;CjEN zFX-KX``#{Qm6vLpSoBWQY0_%gn{7+Z#&L`3X3_8;i9!Z)*JOSc%*|F_=LnH(=3tS{ z7I)(+uE`8DA8OFm=gG1>J*|B`Uz?ESlj+OZr`(N(EM?`ikef7mLZAiz-BXslR~)|@4rA1jc3KhqfZuE>Au2BA(he{wNBA9n@*F=*WO zmMHau5Flhp^!RO5fF0L+p_&;hrTGFmV8AojH)eYa8)$d4GfPc~$@@M|j;R%QakFFd z1@}n!__aZ3GGe-L zj{weKsS`u4=ya_3EO?X>M=;!$rM+?g_(L*T5a2a$n5w;hw>H&|`Xw&d0;SIrQ78TB z`M4r9|MgH789XmVjjyJ<@X2i+xeKA@D(GxWwEmV{QrNZqC%OYrRJbfJOdg%I}|Ip|88t@k<|1 z1Ngc`E9{J)^()O-fou%(n2C%#_X^*djq=22Pw?r!Y2h&^8<2}L zsWOv|*h(u;dmVcw`?^z*MG;Tc`^$^-uFl}Rc_sgmI%%Vb&Lp#p>Bld~y4}z1{$Abj z-;VFShP@#au&Wd$4RKanIlpzkdFcxj09Kxoi?ir+?WvR~!^thqb0j#?V1Y*1jADN4 zQJ$I+CW2!WQ|U?LFvo)vVecyCI))yW5z@{KU(_kKghD!YwA@;@>EG zDl6?Dea77-{CwP;k3O!0;@p9sST3NoFM9Vx9JdEc?5ffrvZBLs`mx=aRdrhA1n~5e zC8#MnVy)VrlD2qcIA}B29HTj&V1Lx4%5_%kMBXtI1uVAe`72NUD`_eQ2F5=PW z;c+mIkjhy=k!Q61QGj!}nbXLeik#K|P+YYz{(#z*VYSPLUU3jZKKk7QEuJLBw2CG^ z1s}8KZ2M%6LTi%iKH~AOK$B4bSx4^=hstL^Vep%Y7kR!s9wXTPf66Ua>PB1ED_}X+ zbO^TI_GzVJ4Qhj20yta2lO;-Wg=uHM!~&T{qFQUVPE&0wbPrUw7kWHW9=ks!b!TDg zQI~u7XV-CpBn)yW?^tW-7qwVImUupDT&`M5h(dWE6d|fJoSVQJ44astp^*1&uBC=D zp5L8*4dx>ojS7EgL}HGaq_llI^B2uGNUV6JNZLU%pGU$H5Gcpe-a8^#%VAYF2+XhX z`^IcpyjI0KAu$o}*L2ZgTM~+We|r1VAy^Zb|EQ2htyP`zQe>}fv=YlG zw%R4MLb|b9THm7pmw2G0k*q^Cf(zO`PF<9GvF%2xvg^LN-8NsyTWviP_(3IoGy1W2 z`w4R%(VxvgXoj-G=JQbSbZuf1yd$Yl{nB*RSU>_5;=$IHH_r_XkQF}``Wt)LS&LS< z?8#HAU7+1ry&Y7V=Pxya&V-As6&i#he%J*)Q@L=@3YEK{L%Ld_d-^xK`T{Ns9XK!L z^xSGq?n0VsI!5;Uq0RW-yaHX#Gap?bqh|s_uskRLq6zGfM9XR-Q7vaZv}WXVvku(A zx#otS(jdxgX4_S2HQd&Mt*G0fA11t4Opr5p#?5ws4h8XjruO>QNY6Dh53{zOCJ#n8 zOI?v@Z+ztFa`|I!fJNpBmC7K!v-eAz4d)&Vi~Pm&A`ayZqa$AOT7L_x(+ZpZ ziVUxnzfxV3j5x>`wAMQ?4Jt}_dw+Tsko#ApAj&Mlk-0jx)(fC*j?e+?o5;CDi{3m> zH5v|iL5%r7XofPpVxG&wwQq-{J0kf%<%k*Kpd;KN@QUi)$C+Y;G+7`GXB4mYD_;0{A6$AirpAwpb3=oA1;fLe2g1 zZ8s*oJh{XJS^kGJ1zZ2^$jo^ZsMz8iwz8E?d;pM6s|u?54J%e9(1z7qE7^Mc8H~Vf ztDBw7Hg5$g*@`n){0ru6`DFyF8V>Cb$nK?=Y=^iXI1+5Q0m>pC=5dAM^%Nbi-B`^rv{w=N0}C=QZ67~^XHys@c(?X`$c8|^d-mv?YHZ{RXQM$mN1-I-H@o!HBoiogHwVRRW8P{)+|&_ znc`*Z_;I)?v#`@a-zuF5@K|^GmXhu+JieO}9REB{3~VL^N%4pM4}y_;NVy50N-Vm7 za9C9A9DPG`creiAeLMistfc$`YyuMyRq~<3RpfCmEsCZ#V-|&)>*^e5;Fqnr++<8n zV@=HVDpkMl={cj4ylv>7H;!jyh`|G6eqqRHuPr)=0=Q_oxBPF4MZ7>aY3s1*|I}O& zbpzt~$e)in)vSdQqH!K=)u&T0WgcB*VJ|heu^RvU;m))d`du9q*}NFKd<4t(Q`rzO zOdy0_P@l?(xG6ju3WyfLhPl|JjX0z=7Xz&39%GthLKG7b~dg3s(-8HN}05z6TnKD zP~H?u)Odx`LaKQ8(Qh1$$x}5;c>YizZ&I}-fgfbxXK3jO zNQIAxTj~~=I-){9!@0c9+~(=%@5<{nc(O@%E@=@_rA>sOhWsGftPE zr(HDKn>7`=#TxE#Lt<#WV(yNCNf zO$%=LAp_b~)N?b2GzeFzn=j&I3@M>!+T%1{s~V&UmL%NijUD}6wj5E;*h&)QEtgiR zw-zL}3q=xST`>e@YNf{&Pd#hREZ-@yWl^lq!%SP(mupZ^W1hRBP4#h%t^Wc0?Qb8m z$5%r?uNf1FdKzexkAC6q0Ap)!xe0-T+iX?#wHaOh*!sk|+d#gXRU%z}3o%_$5YOWt&XGM7q z3trt$4mBymALf8qM8KxhhpQyHzT{`~V$FU_Y1{ueqJLrH^_&-hAn8y?kD0r~$NEwtHOc~NJ7;I3af|a%&Pc8FEyO+Y zfS)oP0}@;ANif0A>*(@E`8E!C=vK2!z$o9Wu(X5u*L~uY|2om|5Ko7sb`@jaZx0P1 zD3ow(m*-L%LG4?;5tisNJuQ(Y#AkciK_IQ#L{xAHSg?cnZsx|Qwq|iyzprZ}Vq(2V zXOWTbI|^WBDF{n}i=t~`h^rS#UHYwl#dfVPwuG`@chCDK_*yK0{6|ErP^g&Wh0=-_ z!6?a1#WZ@`B-mtM_{s`f*mM95w?>Dr4f^j&xpc7maO4h9!Ut7OU%ZIh!%StBFOG9p z1AkDAD=Vz8^kR}5L95!|6f~DirN$tyj zFb$B*!TEOogX^So{9orMGpnIB^uStYXb{^XbSc}kV|aYcEEK1Wdb;|s5Kh<~7%L>KFfHopJjM4#qlmaVG4I8s8O2rgmDO*V@yA2zoZ&wRExtbr zg?NK>L+cKRtGTy?S>w7A&b3hh?``1^3*%*p^he!HbC;y{V+}^ea9t&Vm)!~Pxyc2z zY+b1NSuIz{!62`OmEpu{r*>6kk7i4j-?j9bA*VZSQD*Gl>CuehuQ5*c4opn(^S-jr z%N?hgrc15Kcqtd^aV-e?n4N`SfF2(xAB9$&bVr44OhMfHeH@_f$iqM+BpeLuY#&nm z%9GtwfU19TMeKwJvYnxj3AF_$<3c-d)pr-2V1ZL1)kooS*6I-@l6}Xt-r3NTFp~6u z?WpYR+LFJvU2w;7>HU{HHW;t}Go2iJ`|rono&iP!I`iXg_9MmL4t*m}v7<96x;U9i zMG{v5jf9X41)-jp6YRKV8E zb*3I)a1JkV)+Zh`eub*q22s%lW#BuU;Wf}VC~%r!zmkH)4bQ$=R|3tpKsN~M)}_Ki zU7h>#A2D(f2t}UFKY3XsBzxd=O0vn?Ewe+wod*d>hkr|Q=%@vg3bs)+@g1|D?=ZiywkvK&H^_075d*j(pKVs8P{v7rd<^EJsg z{R1UYXyEskh|u#p$C{VECjY;ERM3X&$=MjQi}#6^Flj)oIMyZfeP~tOaQ*Ol&ybqE zKb7;d|40r)c}DaK8}n6?&D@_0M-S}_56HES{jVi!A_yu_3{zX?ePx=%aUklM4hUcz z^NCI6uOPf$)cW*Y zd9*ddetxU8Jn?w+p; zHZ0FR_vOippDk#c@p|(>=sy~W-{+Te%6k;8(8O=f)R%$3HkugSKUhL3JO@Cb(U`TQ zDww@yPNxNnR{%`rQRqDaUW_L{Z}N42>6p4Tf!{KW4d3SHDLi$jHK@?q-nde2qnz3Z z{0-LO);M5*O^~{4gVEslhTNk|b*Qn=_9eCDYf6IE zE+HDRk4x{keNWFDpnlq#6k4NwJ>Cc8A15Clc2U7J3!Yy?L#*}3ka%`>R;Syc9z*EE zpe3;r952>dEkig0TInZWdL~Wz$>at1I;BRjHJW5D;>f8h1Jw~iGT%gl14mx(RcR?} zZ|@={>3{jcyJ`K`FQwLJO@`SFig2Vwoz0^lPjfZJl{b?@*)fvA#=3)-W`Ol&ZjP8> zGQUvTvbP&`G~Mffw&yCv2U$l%6E*5DUo@jzcB?@5EHr<>i5BSXR@K8?E-+?**VO`b zyw6YKvbO+9>2K%E7!+Af-^MU}oU_{mEWvUWynL9!4_*E@d}AXBwtS*-U%W`O;@;61 zMZjz{(POBsQsz&?OvW0MzTP!7p>gj5ZJsS~>7__h-w{ml%y`ySV!jj}$EVtBk; zMt={y4-~|Xu$lF*wJcC}&*LZWU2*|^n>T*>{2beFYGWg=dqF>VjPDtaZ$OEHyMgsi zlX3?fd^a~TGXv*|=VET#@IbVmKX@(WJ|uo?j!l1TLry)9! zH)NXZkOFxz(t!{Qj1GL)a<1fbthO#`#Al@?J~*sKO7Rz1G7UyU4ZG2rliNAGMKYF- zP|a>~E_0yiS;u8n7w_1^!?;(EN+{X{l>p*OU#Vv5KY(!4fFfhE)jf=lo4^wSwff2G z)M&8de#ojRv*OrQolx#wr4$!kVyW`Y#SF&a2P#(tpQ$ zq7pdGM9G-pABI7NZ!HJj7w9$zLg%$|udJ<2pHy;eRrI#+YCv)U+Y^ToMAQ@x4Q2`NGnf zAhK{d%C;I7Ex|TAJ&il4-ack3r@8Vk#JFeV5V23QC+t($n9=ahvOg;1bKq<>7I*dg4v#*YAIrC6?tPwRchPh z7WuFV6YkKkMFjcjzmpxitje1VLcAcxzGq8S z)c%ANmV8=?W!_9D^88d;Gwv$?y5|vleGC44sTRhfRkvD<7WkzD#Y^v=U_q)J95PVQ zneOPYgXcr*C86ijRI$T`dS)-(LngW0!gzY-Ik1AaTp(a1%19{g(fxv;ri$AX!Qz}m)f0t?c2GdlpvtPZUo^B;~gy{)oAi^To|0F*4%Vwqwmhz+q z+5GrzwcM7(=Pmxn1#M3aB)Lwb-*qgh-yv>kU+k-v2Anrdh#{E6ZiReK1av@VLw2R( zU&B?&&A9kQXzB9{n9>x3c%A|gj`!oYTm+4+ zZZjkU0Oy#dopDJ!qhO|N$7Bw7d;y9K$&GvsmLJyJ5qef5@r4I?A zz&bp|NTX3@4o7HF_D-a=$RjD@paK5kvvGCA9! zHB;6c9bc2{<2;TgC(Pl+)=V6(Jay)?RZ`$cj+|&k^|FcOBz4BM+=OwPMXiVrTAPcd z(~%HDN(})mFSmWxR}Xpe5Z^^g;vdCv3(KxTw%*YOTy83n&o@N0&T^!|{~rriIHC5$ zZ8_KETyqrq)BaAARQuoj9w92&%ZraJ)?3W&|Mh!$S}P`)?i|OlWdA>?2(X7mWKO5i zRY08P{CjWU4+65AHoCTZ$-3HPY;7QUC1K6_!V>bgL+Coab9yKcap-DBbMeID!wBze zxn{azs+8PC(aMebDIWj^+tBN^DXqjV=Djk;>LlB&O=Yk*lbSv6yOlSx7Yz#ZO|c8Mk~BE1l#k0y zyKmao{MJ;GFnA^$2Nux2XIf>Tm0#+7THoza=j{DT9_LoTS{0phl;dXm$+EjTM*ZI7 z35^crO2rfk(%I$rMmX%gg?}5Pbb;?!0WKy@P>mp$#Q`3dF*7cv2$s>9 z-LLFlc!FO^L@NTX72gRoUMRI@|4zF?)TVcZMpeX+zH`Ln{r4&ho2$uXj`TZ2a=Smr z1%^zkdH%^T=V{4V41d>#$rDB>ji)Zy7uVOw z$BchJy0K^E74-a7_605Yp1#B4(NeAq4~)3JXHh!f6yYwx0Smri?h=2>d{7ZU%qL?* zz7lx~2>bgMnJS!`+hZtPK`19^TJOJ3EVlE!EFl+wiqn=Jrm)9fE85f)$w^1 zf*LiTseBESp1oS#iI*b9aPhY1wA zM)11uJ{Dpy!!&KKchR%=RAvDt%!PAm6Jr>b64DV7wFVaerdChNjK0zLZ_Dy{b#iltFQ*}>B$ztPoX zYbUt?a9V@?-B!f>+gG}qnUn3yU0V6$u*}WaF>v|Bz-y_|1puRF%?bhG);YD?95a7d*l?G?shRcToe-VxaT+K) zb(x;QQ#ywD@l=B7u9D5o*10r4`J>aGbcA?o^Q%cA-1X?%2CEf-{7hrx6n?v|^;ZHu z&NPRc!!N3p+AcplIZ+Pa;z{7x#AY@BQcP;Vkgd^n)SOtZ%X*aIU$Jrl5i@)~E9Lm& zt)eaS$?OmMe41L-)T50#J z+wtdu0L83lDk@G2PU%JJ z=BN6}2jlKGFEqM&Z1V1HtwQpR~{4mfAqLw*hd;0;6hTDHG5%G73mBz3e4TE6^OED|1$ieQ^#(8zW z8fhBA!#$V_G<}f~I=hU3i(z>Xttu=^Um7ghYpD2#f$eR`$-<8jQ{VT{Ml=fA8@|{m zT*&j!Vs&;8t1Qvyc#YJ{3cZIb8&A15xfO?v29a02^xX zCseqnU+(s8>`{l|79Eajw_uJrHl1&Ax3ak>+i1_vWzbLW%e&o-N_tzsaV>hRY)wyg znR>enL=rjPE8xH8tI>^2s2_I3k7XP*&}al*`r^g0X$k)W`}^0o)e&|p@19Bb%BKkV z)mS^2oS!EHb!z3vuu)av?V3jh^w`-?>D)b9-$&`$pmmX>747+c^`okrTThBI)~{0i zUMYutC);H?kL7m`5j+CW9@LGCeQk_`ZCC*LLS>rE^@{E^`QZJ}>_oY7PRnR!1S$O? zX!6PYDed4+9Q!8R%O>*_xz-Ye6k&@^ca%3B^>EyGRJkE8xR*Gm=jMX!3DjyUa;s_D z2%ICzNrfX@13(^2?3?PCeOV#1S%)52mIsa|qlB)%r)yn<B?OH9lRmi zI$a!vd-&;fxK&P<+<;~_c(geaQhRw(vh^!bqsP-{ko*=;Qz~@;hi~VI{GG2j;y#q7 zEx1mJ3((Ov5 zTH|T`H#;$-^>^uvyy+*(c?wn*#_`Nej|Jp*vW?_gl7_8ww!%*AqX8`=Tc0t?YBesN z7@{Jg_3){ffKIKYiS@3j7Fy7yHNqlgHiZLUveV^87}Bqh0=@gY!! z_eWM<#_#7qkoI_&wiX1QpO$7HrV~A`$iamE{VnddoRLM}1Vz=Ba5>WWpHcm&Zp!8} z=u%dyvhF)z}0GxWJ6m`(D=+F&qr;&bTvpuMMQ5_N!1VNuUEk;)fZjIhcgY3+j zix)|EJt|(|-}9@f!*@qbx-l>twBOF7P!`msvu-&cHzBX^&`*`Z+ebOQ`zKffaIr=} zc#{a84$f%{I(osb1R`u5%z3l8Ml zizWV%K|c+_31whsZjCK2KbUFZ=sA?tx375~p};ylZ~F3lS^Vr(!dcc%dJ8~y46EbQ zaUrfVk8k&Ts_ps2=upry~(JCo_c`h|Q^(sD1^Vemmq; zOq$PIzTFmn)!Wsu`SHs)47$S~<%!c*ckI`$mVFm1bRoPA9T@Mvc!OuHu=M556N0W} zV_3;lCln)=eTEcEeLwG92e@nU$L}<*20=^e9>fYrWCi3U>FzHE&P0-a;Mu>w8IM2G zq6Y{T+jrvnG6D-yJ4(Ht;W*O zj!rhSm!97J;lQ_XPv}H~Pb>Z_ygg{WOj;QAsGGiVdLQ;LqwNg!v1*>c6}0i$HXpxN3-={YCYR`-?iZi zO=rdlT)Gp$iW{nkc=64bNqPMls}X2Q4vs4DFRuF zii${6>4{}2R-z)k7m4&H9WshamsqG$qM)GCL3#-_p@c+4dMA(oAwUQLLfRQz-}jxp z_c{Ada&b*2lgT^p^E~(OzBBBO>8c-jGC`VqdgLOIiWSi^k(K{(W5(sc$qF&2WY)$e zfp`*Sy@n85zdcj=Fiy?QP%UK0I_##dy4DZxOAKGv13!%V<((s_tvk<9^J%!A>hpde zkKbFA=P`ac6_tDLavm!DxLC<>ICHRGR@a=#Eb1Rx_NmWrT0-{w$`mnh(QANqb*%*U*;QZYHOy|SB;Z5G@A%B!gi zUd4AWExoxns@WWbpt7~?14_Vdu>(Qx5m2)Zc~rW-C)BW5wgToCm074)IuVb>1*i=?SPQgk5;H zT`yBJn2UE6CSP)DsPr0hSvb&fZEQKzG_^OJHk<^RhzRTMXNc6`; zPMuLZX>FYXG*r_!l9Trt*x9N9J6nW|pHxEyivNFR9A(Wr^P*N`XtLf)9I11 zLj_dvVO;mR4&nJ(I0#5HXd8>X%D@mQCpo zn@mgULo+^SSs6!`j4B=PI+P{G_IXfbnxk*p$r^ zwBr<*1uHh#-#m6KMku{KU5FFy2k9d|C|C zAVlOj)hB6?_T8p3vzKWPSe34A<6Semo8+8g>m8JL%Y9~x5znEmbC%&mkDbvV$U^vY z-h-@RrII`3YCL74uA;0_F+NN|9OzZs#!k0(HQC_MV#GTwUc6SRU3X2!3@H_tcs@16|VmQ{ONCRC7B1bd-80)H*K zPq@$4x6VCf^93U+0GYMB9IOOjikZSco&4W=fUl0e&_a)WFx$c}6no&Z)gj#LfeR}a zIKaLG9)E!41ZuY@j#;nO-JYYaA!0iUIR13BV_&ECu(8Zr%-Wpt&raOKHy|3|4-fg5 z{fpye8^B~AYhw*AuY0|Q$}-kNgUqyF%rfCHJ|hx#7})=Sqx25SbdR2G!X7~i7F z@i{+_9}H8MhH0rNA5mZVEV5eqv;2I%`p+lpzb<3%NtN5yU5CzeY>s|6X$~CtSUMZQ zee#<512I)ClGjjYog_@{sULrEdkviD0J+bRjN%S>Y zOvqKVo`@>0E-vS~U`H9_!|9Jn+woVHa>bZl=L^U0no|ZMUKZ9%yLVrXYi->Jaw->H zmxHc^XxtBSo_bzuMb%l%J`aNO$R!7$<(I6ER@Ru$=%h?@Tm95e!uz@C`_}gp0DPsK zU%h5&2Xn21gA0*!9LP9ICN0IDhgn|U6RmKQdgycYu~@u<2xZ}|2kpqNR#v9j=N&Eq zxR5_opw5oN;)GLfp`E`X&7*Q0*C~DvigsjO$=m5ufLeJ%&GPFlt?9p-H|ZGkXhg@l z-@~CYC0K~0=Kg5u-V&V9jC)hK4=95#aBlmJfIWWtheu9>hq`S}l0ANzz|qKNEbV(x z_V>E`*=?pPq{u@~4OW;8U!CTi`~1ZuydNJ-*a5CJv}4#aDB>l6+~T>$mZ@9IPftd;w% zi9iV|!VtS{NvX77J~>}5n^L_V8@TF93xYQs`4RY6So>U{Vdm^jQL1dus z^^8mwDmk>dOx9rp-0`ee8Gvg$A(6ePVQ_A_PziFx!nRX+HDeMKHM>N7`3qD>MUHwA zq+#~at`!X>hr$(2CNqCC8EKyFA9s|>;c!pso9FTL4#2$1bH0VMM=oHPuvH>~<-#FH ziNIzlS>Y`>{hz`Q_^qch?~frR04>FJ?OS7z|E`ewUj$OB7wl>d)s|cR20d2y(n`QG z19o4wKA^0YI(d60bZdNVU1bC-^{Pi&K6ioAgTkfi>L;^9RwCgd@)UOZW@(1kR0mKW z^z!j9_MZEy$|HmEM+yee(aN4>ZRYs_4~c`cp>c;NA6!C;;vgmG5x5BZ+Fa!^;gER8 zy#@~pAoBsn+DERCZ!RvCq_bW=aQE-Pp|mxi8swyfCw}1WA(aL<4$jjnFz9rv?~z9( z1)obhD^5r|g>%lF&P&bLBQ6hFi}V4Yl!=&J zr)nJ(oAVEpp@W%lc7wv+-k*@GQ8FcsDu0fO>n?cm#Efv8J7_~Jp{U;+E}W}^R`EEE zLBeIkK7j&ocQ`2pp-J59&$6v2+zYDPO_se^BV}S_OW1q0la$UdxOOp5<(~o7vz322 zh!ln{8tc`}e%U({AjKy~jW0=fP=-I;kc` z!7}~7DimCMLK{Oe$|v7{7lQ8yhORY#z4;+TTUl&fEah%;ug=)UAHR6+j?NW9iy*zM zs@uzmE-^piuAVQy%1b^5$6X~ZXtzG5mbe0eBLvVb3jQ^5uVrmez77_m%BlByqSY3% zlfiC2w#47|7!MV(cvhqJIW%ZX_7pUB zIgNM1(w3U*ngi-me;xmjUjYB@W(E9DJ1ckyZtmTT4sxGw=)>_<*7=T*uiZNCn_IqG zAchf#4xjlNTJ^8u;WyN=oNtD<+9KRD*7sRcS%MJ+u*u$lP)q~H@_ z#&@zINsk(eVy|834O|<2rg-$sco8$t2n2@vreljCet5vTeHmi&NrOh zS2~J^d~d6^J2=V^R0?$SC;&7bUZCie#3T^h$wGTJDd(@BT^e}yoc<)tz!#~QbpuiF zkgq`89~Jh$RU3axpB+Kyo8_)7GTkoq#r8z%2(xJ~e^z*;y#@{pLCf035o zSufonaL^#W<@yBVlh*GXDKe^1EjO02D5QxG*I6}!o)g5SEvPyH5rmnd=!I_Ev(CTt z*hC#Gsf;rYmsfyyju15E>T0oY4iQnD*q`1RS-@M(};dO7`i=dCmhy`G-=`$4&hy_?`)lF+w8hmPT;a_$hY$UbECodv7 zzft;>u!)TMzfaR`diWo87lrf|GI+6sr-E zj6wW{$&;CbZ5?7bJ>vW>7t<-w%%v3$9#B_Dvu8E7W@o{ciwV#qRRI4@d$7-s+hC7) z-En9WIgf}M#{5satzU(LxRn=O!H~KKW6b5&6<$n#$4oAVXI24cq?;@$c=u&pus-#X z$;?b)^1{A3td&-Bb!*U(T*G}GHIq|oOw-o7`i!fKt z7o2Kk-!8aXBEXKmS6wLPq%myh2`ygvQ_YIl(Y)iRRvh_=t+R$!zOAniB0*Cte3R6U zi@2SV-5YrUO~{%K+4Cvm&G@rAT`N7m{o2O_JL@23C&@uSH2dxpr~95>_fHv9{X8l# zQxd>W)`S$o1P3CZ{thl$(Lkyi-(>|D_!pvf-m((4y4ktj9;}qs zLcEW=e}uXn;Xx@S4mu4J_2yDOX7_+&=6hWMlYtN!SYM0&c+l~uOllVCGEsgVzLuyA ztjeGC`3q8fbY66wQz>guv)Ks>3xyN2j-@n%Ra2T8+ez!ab)Md8DzJ%o*;^Pv7Qds8 z_(R98c3pgwJ0J9SjX9mwW39p8p;izf-~YX%!e%JaaM#t= z{{mG!OCSnlGe@4Ahah)MTOj`^LK~uQsGC9~QJ03*ffe0f1?^Ttk7VZre_0tAK==Oe z>M6BOUInZR8<^_O&0u@qDEYaIq6`Q<^w-z!V9b?yAtRDS3%|_x_qx9wPuvJECG9)| zI|*7<;s3AT>0i{gOWgx-U~tYgw9aEA@m63$Ws0#kh1MeePhkM==5K!COvss`r88#g zdUKq(yL<)SF?!4TJ7<6m67<~`f#BZ%DCggXRf?}Mdnn%@ApgQ6?0Gq^&>LS!+5e5O z*eWc5zxyf2GvJ&>Gu{EtpTzka`d^6mK&Zn$6pOC$H$Wn9D@p{4FRi;S_H-M6`U<%u zC`n;CWqv4&`e{HGn3)=YE}YDH2`uG4M>RJjkfPXSi{r01T|jWX|MSP;_{pYp4*+zQ zidT@|S6%{L$O#?MuMGNzh-qf&?FclaB~ZB(l<(Y&@l!IP*95Jamp}i*QFKQ&I)-L8 z8^PM7Bjm7?xxvWB;Mu3QL9{hq(H2q)~>7wrYr@F9_b+R z*0e|CD#w>szH&ET$#;s@@$zTbNT%Y?Vq`V)M-{rmB7pfy}R+v|JJcnmb`%b=U<`JrwAH4B1VMQ3W$vESK3>+{BvE_)Y0^MByB>3e;{zmq=5})CM zv}CY{dDltf6#`Ny8i%)7%4BZm9@N%4OtWJkx2b=pe>Y z(s;o7)tMNp&CHeRailRTvA_P=+%HZvidD4o72rJYfZLkGcC#mBP&!t;b`&pc2*9k0 z!D=~@c~_%D>vs&VzeV^UAqA|m4lV=huZxdzvf_xC@^}8M_0w^i9(cWj)K|Zskb;>^ z>q~7mb5u7Y-rd-aK*lU^#bb{YMx=USjYFF~dDk=8M&qLg$9$kLb#J^&qb6n|6=Ows zR~=57W-D2L1->9U5K)8ZXZHXYsm4Fhr6j%{gw$wzN?u|@a7&boxQ$BwJHSMhFNml#pd z8@NCg)Iet4lZDw6I5x3Mro+UBkLJZtJJ9|1Z3>*>q=8#c0}XJ26eW}_ zcsc`s@^ClG_(qq66%LZP->;<(6l@%t_4PboR2%<(@L@Q@9FyxE5PwiRF6&%|fdsDHpeTjAY zPH$um3m&`Y?kDVc18wuyTHE=c`nyyBP5mfrU#iG1RQ5Br6qPoTp@e7<@aX5I)&&5; zr$tA0qxNwM?I1*tdciIZ?%(;;i@|%vtNdCQWvqF&zwps|EbolzZ369)MI4c3(lH*p z%-P^P&VZSD3VtD}D>K}jLHFgAZ-9@^t2H7isW1UoxQvP!jbwdHr{-``0CSr*GJ6o; zvvC^k+=#~oX5!8Ajj;xy&CAugk>lROW9!y&L`n?mDp8Pz?^kp8t@9+Y@}0P93yy1? zgXe}^0z3&Gc5c?Isbu?CeL0p(A@z<=PxG`Y3deJ~_F?mdtSTgf*P+bzzN zpke&+udCl3tJ#Y+oxm$`%=sHsBi%mJv2P`NES!d47YkUe5QP2_EH152W7dDzYh4arn~qvhLQW-hs`D49ooYn%IrJJ1_P6{i{sbtN zBSANiFG3DLCOnj2j6VNk0jJUQOwrRy0p#?}XS3qSC0$_V`C|ROMGVP;)z@GDF@yx? zd9%9wVV}H+Rg7Bw>XqXPuumcO(-VLx=R1toxcPD7)oiCK|K2XXwtdE@M>3>$rpVP5 zL)T`w-I1|OwrD-CLWQr_iQf3Y?qDp|WsP`|<2M>{=MqrYMnnD66!|=1OpGQsh{dxG zb*Z8N3#XYZaTxvfj5UVb->FnbYa9$>J?-*6o~7aYgA@ra57rOPr9n-3JIdLqz*>K2 z%o3)C^Od5TTe~!suoCN&Q+y6lcCN6g9DwzqefXa%U?x_ayqZo1+6%#g70K|45ME#} zznHyv1`_VeHFagE6<;UTQ$tvy7}iDLQKl8^r#Q937ZzR(V>PbN?P)`|LRs(O{C6{Q z89s#!8`-5sDNYiEu0x|%z&Od9^-NLO)4)0#d9rUQ-z0Us+sx}K6C15KCDxysM70s~ zr>fa&`tB;rvu(?SWNrGbB*|!oAHEO83)LJCoTw=pFzM%r0@ndBkDyf zU-tF60;;eI`tsrTgLMVKi`oaD1)hH})DPYeF zwrrlxnHQgj(<0o{RO;fIEkgq4d^M(4F2u@TA&|HmtL?+gl$UG>1o%}-&ZqAGCN1MV z*0b%(#!j+^c+*d}3yYC+zS4_R_lPxTnha04SnZPVQ-?C>e;>&8um^m><+=JqY75g# z$lhWM9BS(kKoC5%k&{yrN(gAaLDt~CsCzXhq3%$(UByofgGkU@2!1rS5mfPN&Sh^- z9HB~=uGLBUdw5(W!@A6xxDs2TZUh6BmqJSbN!$0blu=E3bWSbqLaebqeyB)0Hx8&5v7m#-{@two#e^hbZu)xI#>iaqr5WK7ZYyV)Df7 zx=zqdd5=N#qzR>HAY)bzo0>V)JlL@GG-&!~@5&&!4wj`eS*b^O*f7J8iOr}w=~AZM z4%6+C?Xnt(xpwdVL4(cbCp~_Y>wZ-*$8c)+S6L|)8xEx}0;Jzi!C%xhAx{3f4v@!} z@?B5qnYk+p&4W$R+aYVNWx#>C zljkh=n~&&2v1=#ogEx+-^lhl?J(2b7@d#Q`k7uQy(e2T6$KIn_JnOijqoR`{_Fe_? z(kcxS{T>p}Ho!!=B(INX4{KZl+)ofhmUm=@G3*k43I^-eS_xql1>PLG@kg$$*l>P^ zoNLj~reWrX@9vM68Atns34Y#dSpoRbK(YhqcTu=uqwX$hz5qprO=gjz(#G58gYsJr z=6J`&uue9cN|$7eE0WMFgH>EL@|xlG^S<{?oaDDe62CsCkebEu(fff2QN}uGg(Yo{ zcsf#N?;f(@NZ>UIsJP4qTGv}O7enUhM=8ABxaJNn}NC{SBbGHR#*W%#8Y7>7&U<@BwmhQF{lUk51|uxJjMCkebY;lFyu~{zCf%d%K>X zuh(>#sRM>1NK`4AI@zyR<31g@9(Cb|bWf);kg!D}uBCs=vad98 zTcj{!SWy(IVscOLojhxFN66opKxk=$uAPbUa4%jU44PjEGPTyF(!P>$?>9TcNkIPe za0+4XmhMH~fkZK-S;vYi=;M3DzQyLFCdJe;hX(ikDy_i8gX*3nupB=_(U&!F8NC#4 zxUT7A1v=E`-O2uC{j$g*vaSsCusj$VKgw1zv901R&Lv6qrS@h>M}6nA7|2BgPp9C6 z6+}3moCn%MkG?gH6a`EiUpdkq&cz{_6O zu^?nXV+nX)x-{qTw{Qai1)y4cJm{W0J~{D zZHCXW8xP^LtwKIrC1d)P>D!ynyH)tNJ6rIAeY;JxUOt_)bASCyg%p0f$bly4neI{j zJ`47V_e!Ki^4yeY$_{VegA-L_Q1=Cz5GFTg+v2tI!eaEO4>`Zy@>o&w?)U=RNOl5s zdNdZG7aTUQjrNzq^^79m^Q=J+IELqv_opxBYjcb2U)GPhPEl@_3k5rJmJJ=B9j<@# zb=gqF_pxK`)}PY#Z=&vhDE>P;nD3?iJj!leOr2WPk#`B&>Oxj=xUrX+#<&w`bdI$@ zPQwmsX^-u$y$(JLT|R`iD+?-%wfd5TPyj0Ru$eY~!yB+2Exs=M1 zy(6#fp-d9Y1*SV|bMYFlr!F5!p*IH`DOK*7UY0bN%$4q_9kgwWg+LLD82Qe2&|z0w zAELBA8d$FLPTrNc@M@W=U3CsVVbb&6x>yF%qteMrPf!wJ2UV_p;rOUyUymlrSg5(bxP_)q`HGBv53&K zoR_}GSiO(^JO!wC{zSPQ&uQC!&dZ`6mP0;I_6z-KI@AIXC^SRuZ=g-w2Fs(3Q+1CU zYIBDan0a2K_7VCEbFB&!E?9$XUl=TisIyesOw&YvL7@?K<0kuQ(c*jiYn$4~EcZ&# z{w}V2yKv(p z2rRu|z&oI`y2M)PeD~ruA4h4GJFe@tu;SF`V86rg`gcFw0coh62s*>sPtY#-;1$}Y zr$s@;PG$X#FuD5$M9$xe+81e3kL9VA>(9Dtt@O0-yNZLpIcI_cz>@Oe##=O}LF*=4 z9y8zhgnwzj_^Bm}?<&K=1o5}iu@o7Sz;dGwpc0+Bo;&0DTK+pZQR-F(%%uY zSYz&=2)SmN+k-wz;`Zx!jni2D$hqcg7&!k#2Hk+1w zShZlMR% zBjkETXMaJG$L3srE>{S{1>Xvwpc0n2*8uS)uiDrARur#D;~86W1~m#))ZboXV)#(k zVfObf2rCUSTl37?bKKI->J_jcU#*TZ{>xhLTK)FyfRO1mgW8|#3FADuWE{%*9G@my z>*pNdQ~O67B~3~BNr7<<*EvEPM*X^-ao_7phGae{D%F%Jk$P*|ayKxbHUEbGXBVWl zC)p+CqanmfbGR9S*Pk3QgvKp^!51pd?OM>#o{)WTbq&dDLLg`n0T7dSdf*JED&dej z7DQ1=aZm*BpRQ{f2eDTWDC*sg00lu}C4a5c8)!STo+sz_{5B6{6x%{oGtMJC;5bmb znrd<+?D0Fe@x~6ETjQIE&pXuIiEmKyedVg|AZ_i^_QVC8Jo{~E{x?hx)Z~hQiU43n zPH$=lF6d96^p^J?xF4^Zad<(s&Z~}xK-y1x)TC3;@!(rsSYL@vhE|YicAQL4lQ`G` z3b>WV0xg=XK~vHlnrRE%R*^;!Cg*u=afCu~q?b&!@YM86_<)Ib@^J}58y~+~*#@sY zTdJwjcrg_Ea|n-A{a){!tb~1RVELAbUvs(mZ!`O&gRnl32)Qc|vs4Lr!s)%z35?L5Y)NWv%TB8MhV-^V#yEBN!{?7kn{-{a|u3$ijltL2uqw+ zId=i#MqmNX$VsVNgx%rBiFn$E#3>_C-%8#Hx_Tc7?T2DybR9`W=`#uh} ziz-@gIA7@3p9_RyA{Y<=`P1ZA&>v^p+S5?U0OM#))(AC)A*~FNlsCz5>vU&v>}8SM zpSl!y0>UEFdapWn{?J!nsk2{VOkXNL{7^PlhdC+S506l)5DRDCHiA|iOOZs&A8vYg z;h;b|TxKRg6KXPitfXd?U`5CY(`%)ZxG(Q}(!@B_rkGw2>|)c=4AJ;R>7Sed$+s0? z{)gVqy^cB~=aW29xS*ooqCY^r_x2IAg32pI-36P$J!*Niw!*0Jty@G0t)Z|yw4WER z0ROSVoAKbmA@}56zRoq2ZwiDG2IvZDf`@^t4GO9#Qty;I@4muA%CmJil=7|vw(%X+ z14QI3Vmys#r>&j^)91@pYL&Z>`|KmrSVuqU1^_SJfM9&*l_r3*7f zaxIs74Qp?hFKPou+$VN>@r8FPUr2MD|CGI~^}3s|6dgLqK*dN_7$b6yS< zDYGk=%7Xte6IB)S0=N~$7bOnKuwNdgN-72L?#d4j=Fj2uq+@RT-H6yOClX_>*=O7G z5XekqG%bTl1IIfqjnRZ!@2Nd5X@8uR%%Hww`WPMFYjEYJG-Ub|^OnPar@wU3nGgGW zoL^qhg;>DJA*;q1`lc6oS_OyN=F6Vx3EFr*_;i1NAxx}P07&r&VOE~z*%T$PBm#r3 zS43&Lw@7+m(+UR}_Sd#jDBh?G;ww|ccgC5HsVJNgcsH3KiDQB&}_O$qzMKVbkq_nW;M;munZj&TC4uO&ASrFPN41CVj}M>^T@F z(NZb>Lv`GEQNt%wnk4#4B4^#Uw^Y+)teBh{>j|_kcYp_t$FFIY8W&-OO~zCNQOV(Z zK=J2@M%?EUKFCJth$bVWkcY7n>d7gj>#=XkJsLH~ZQ@GZ9JS`d@ATB>9VM097=kqQ zsRgX8rsMQrl9%)YAu{`p>Yl6FYGbk8r!AcSQnu_>#jf(vAb$S(^#NOF>2+HqbEB@sdxh9W}u{kP}5#N?%ehJ;VJ3XqDCST)X#G<$cKUYW0sdnnZS+p8_x{Qi5 zagR8mKzQoa8FVJ)#Lm3D4KW128gm8kF)Q&;`lCa))^_r(+Fdt)#%tdlWCDXa?8BB$ zv{pEZP)T8*-hAWHfAd~n*7j>O_{CQe->hFf-8KYq6*j(*LI)KsK$svrt^OXP#m!U)Q zCr>(N{n5qTQ`CIe|IzR2GrE1l z<4{g*9J8T|5#<+%`;rW`R!sya&hG_dO>b<;e&kS1kux#|a@oDnqOae55A;q9!cjT> z%k609C?B1(>X6^B)zXWg#rc03chF=to$ShiE>X@MR@lY1I?Kr+fBB&o16bdHhnxiE8L?MFLiWev6*<@dzFI^0llM`G)@@n>^E(w;t~ z)d1<4bjC;0cuT<1)B*bO5{0nhy;s5K!d7h8mx<+CHB2XsaZOiUSz+I6{GVXcM8g_4 zzsuFMg@!H^O`X110N1tMZv3l5ztmydXs_L2g=TR;+MgSK|Iv~>f4*oIMH>CM^o=Q~ z-B}0 zlmnhlj==;c9ASzQhvXdZzUom6X*?C( ztgZ@vqmsj^pxaB8$bd+zqzU>4`3 zjh}#tn9MDZfg6_`8}{oRTPdTeF9-62SOT2Abcr&pujwg$sVD;0JVp<}_TzjU851+$4qG z&hKI$3Gep7z6o9? zgt);}`WoLC>|-h`uLmo$-!UlW#acmQzb>xvQsQYUwkl20b9Tu339S{zPWD#;8I<(6 zSDoEK0cY*>VF+QdeWdUt?jzzA#YMFz)7MKU!JCJVTEPqDtg^yF*o3ao_5><&P`N}?lbrW=UaCUK+uSWN< zADHg;^K;fE|CEE^aFj{s%rel{x&vI$pbby^3jwIvV*`8_OuXwsKPyS_ZsjEE3JZ&( zRkpsrk5dCI28FLpGRRu{lcogJ&RZv;H{TZ;rv$Ct=6_oJrER?)vts_aW~si$0_euM zaLt6POb52Q9s0;+`faV7>m?M}z4{4~)kEl#z17z2&Xu*+UsudoHAh0yZ?U?^H|kbY zh@p;PHHdsRgbrSRx$?Y_Pg~hiAaIJj%R@#4#|xM8xLXrAfE6#UBdBz?A_+jLK%Vpm zra$JgD(dte#j$6=Dv>jG6?F`_bCyic!Z6FhqXvdg0q0gu;R@@@LWqU6rZhA!Q*_M& z$#C~6I}^0mc~ z`M)T}b<2O!1`XspoNC8pa0p=cT1_M5@bR_biH@4qB&U9EkzJZ6@@3Z!&WB_ROV&M! zeE3q&Q}E5))4a-ApvoU&6kFPrAi85K69O)?Q~y<^7uM^jYnL10HZF9!;!bE&*={#) z=awTJ0k=@+u`xMYa&K*0x}IRhFB}IQNlc^zY{H(I()dL^40jw zVXwUcON+jY>PwUEHIl=W^dWVK$(}9CXx1hYx0h}zKB&CQl|W3;o|xci zR;p`;&GUa9sff9MFh`GDzsTJ?Hr)Yb)jyDE`RE|&IC=KeN9{1ipKf=~|IsTVK#@bm zL{(I;{5*-ce*@=}4Dheg@u&HXgxnDK3a9mhBtl=7I7wv0SPMt=>VP~8LTfEd{2Hm^ z@~T?vjHJlnI>==Ghx2k_xw|k|zFmg7h(VhpwNA zDy12A>@y}Yax)Rs;nf}>CjDTjyVk3Eq{n(5g?qxwlUTtj= zX)b;$gw0oM)5j`+%`x&1$U(RT))BrVa;;&?=~YGtV@2ras8XQjWCo3dtIll81h<>u|LNN znTKKu5!O!hdww(4ehu~=IH6OqYO?c%%g(6V#-VoufAw>}asw`1mv%QM&ZX^UELDU4 z1j3?(0GWuxBV^oDR>*r61h|0vQ+$MMjmH_G`qKvkv9a7%9{#y`DDpTdk-h;Fxu~jx z%ldOu>#aN^49PapS2}P!7%Hk^+&GaA#%9tvl3Qp5Bm)QtDOFf zEJjS4USv#b*{?HZg44Ap-zV+z-{gJc9cKB`mIf}k@*xjcVpt?V4r ztz(>kSY;f#V+U)6yOY&`_v4T2f}pdTWofzyP$$IoK?-VQc-K9MCdPgIcQ&>K_5Qfh z`}Hhg3t-GO+3OIDY%W>BV}4m&0_hOR9y3y>vHMmKxdIrXjAHe7UAjD(j^?)KiRi10 zmlO49Yh>iT;Jki2?)yzA;4VGQBb~y&ZELy9rRg-_R)8bm#Mr(|2!q;zj2~fME&%Tb z$A#lm17@qJyN^fNhY7he)-9SBI=Aj&SO>iKrWrEfvegM~t;@sTXu-Mqzt-fP)T4x| zzgcWNER{P|XVVgAK6A2R_3zuv5rdqv$v5yJY{Wryz=aPh>bu|iUh+>n)V2Ayp!jFE z+H=6mSK^7^q@gQd3iOTCcKT7q!5<8#13%ucb;L0L31hqFLkJ{+mU3GeaPdG8cKM~d z)>z4S8yM+)SU4QzHy&#l|28X7iGFvZxU;|Gvy1vAx3I=PpVYoMqX*lX&u=BpZDqE( zk+x#qI0ED#2j>0);w&>1n*yjOG=rjY))qcIE>dgq|LE({mGv38pjqZu1QT^e(B|m( zSA6A2MlZH?RdC@|#U|+=4B63Y5NrLA{vw}0K#Y@$a9O!Lys>R+eH!MO#6QJ|Zjjbyv;HKbjZJ4I4)Fqgfky@T-csVG`e3@?E|YhJt>yBn zIYw<%vb=`%x4vY=GVAsq-0Z*7a4#gJRMSu)+o#1G<){~n-`e#nIH>RH+S;3UGQhYr&p6TkHJ!#>i>JQ2oLkA5?fb#tX2b_8o0c>L59`$MI@t^1 z3Mr;M3*AitIGfzE0pL15B1!qP~1Q%~zM4QAG84OOW94%ETFQwA|24;e012W}HmHKzry)$S?36~xXyl|0! zWg|TYK8x8^YQ?su&kFaLYTFm0Q}P>AY+B|AR23!aZ5}?BY1Qv6v(w9Ka*rKu8Q?p- z6xfQbP8fG5k3F7>0~;x8Q!TNw_`2az_c20Z_X54PQG8*K2&utlq#5yBGY^(-0MGmP z1yk3%q>5W=>L78ueF{jipZ)lrCPf+Q?aK~ecD3L869The7E>}yA3O5C7(6NoZHqLv zM$e4+H>&U*+?2o^Wd8PMNvXz`Coy&9#iR44slrShCx9Tuew{$j@1r`Dl#k($E9^yU*YE~ZX1iXGtmtK${ z5TBr|>&;|?IL?8qKMlFG{beL<{{?0?1G0Jj+6yO>$!wm!U^WS>Fe}jmn;PolU_2cl`nJOP0mB8GwVV%)f4*JHKeo) z5G^f`t1;a}^t}!_aav^i0iPk#rsCJ!w!ZD@9bcdD-cQa{k=ytDDX2el6kg!3XN+=TJ6f zKbpzTO9zvo!lOrEKK_D*5x1YX0Vp!)$BlDAiowO^{?UsF5W!^f^(}MUEpJ>i+|ob# z0vY)_o9U%6gOWJ#^^bX=-b0(Rs7V%i!2^EA#ndHQW6?C|`%{4n`qK6noGv z;v2jtNS;W2+{%|)-PkgAE~6IQLrxDCL2Fp<&M*ni$JZA{ht>a;u#hp7n9cn6v3V2m zg?gKpplPQ-?CfY+VjQyb*YSr)&)|_yh5DegC`-Vc_iP*PS-HpEs&v3s&ljrb)v8VtR4hKt3zYffC`Nln}v#PVx^q1K_vi8*$d^n zD#W#W4eOJ~&d%+v_bckn=(Yv}jx{cYT(ice6wJz%@-GrNj_lY3rFoWU-m&IY{2A3W zrv;loqrVsAl)hn5Oq7dNquWgnyVRep8DprIhAqT3vi`J7!=e`k0`~zY((=Bv|2y_9 z)s!*$it*H?ZVKx(8?9HFLhXrNl2pe<(FR*Q-9-JOorb&&p3Z3G`}(;^lDTKL3^;+Y?P($Yt>*E*J+ z_$j5KU=BeRa3wEXvot9=bTTD^09DLUyQ9jE4AjZI<;7BxMDB$tTWI)ZT4;nmFdlGD zixX$Z`qmDJ;{((jMM~3J}f zK_{JVhFt+0Xasfg0&agw_&*>qNxggX)#^pS2&%P~UrQMnvxz;>bAg@+dW!ZX4}Ng? z?GtWA zsEO*o#yhG?jNqyY3#Cf@$EC=QCcJS1W`SGA|CXD-5nk;v+H=JR07^ab}{* zpyR=+X4~eh@9E?2Rw(dFFnrY{=q-`0fp`g-mVi2oqN$2IZ#)!|3ak`FVaPHaZ~Cn& zY@y9pMzdc>m~C{^Iy^q-{GhUcK}4jJ?M6jKq)I2KsJK-?EcD)c z?}Ut^fFKYR=@3*@R7&U_LJuVnkPFYQaT_&0xsx`;3EJj(VP z`g=ZTsx1_D;OKS5Xh+~QiF{qTsYR34RL=H#;|FM&*fr#=yj$I1$ae4$ zX)kdr1uEWv)(0u`5()D(N{2|F0rgU+8qTK1l%gB6=3Tl9kSBr7MZb-qk~GYjUhTQi zoithz{b3oV*TmINQ)_9sxriffz`k--;-c-_-J4OGCJ++T$9(b1ec_rC%=c)pHhYW5_4ITJ+q3P7f^AC;W zu?_!-t>_f9N*rUvRcT`Ctz;{xJYcUS6}*~PU;g^I&!%dum@9mIOM68E9rk#pWPXne zJ}4@h^6597XluXuBzEJNDP#|ezbR|L@XEMmSrtEA5%2u3>j3yic0d^{)~t{X9uEXD zI;-O}tz@xqK)3hA9xhn^NFHcI1`n|RBep*FAESZ=cmA*b3Q>c%`FJ06v-Z`X)(ifs zS``VwL;lOqJjh1MZzR%{wZyWyKk(%J4@+2!z(poPZk9wzlJhDXfF#peyNyUtvwa- z#DF(OSsNFvVScVIO;l<6lKsma!iD%tb0 z58N-H*9fS~5Vq|8S2l1jklWEAU*tXgCqQO=TzA)Aja;8wzs(rIF`ljyy+nFkEgaNp zdaIuDuCq{eXtz`S;$dER+Yrt7=l@06?z-`ohmeIe7v^F|^rT!aAYT!)>7gwt#v^_f zK;*{LRJccfZz{@!AP@uJJG-|KG8%b-eKoIi3LE!hV~=Zk-*|LjWC{nkVKvYq_@}Z# z(MF2z-CM`XRIR{^S3|YW*BRs~eLs~PU>z$t`ce;6mOhYEFCDv`GhUwRrLb+Sk zB#NUJzX+9rNc;w~`cUAhxij3lIU7qq(s4jQCMbTyy)$cuQ@|GN%cw<%6)_FItR?f= zbOwwhrd5$NzF8b`5}QoZpd}3sBk8Lt7Zk!jW0N+>@ttg!&p5E5;Z*gayT+0LWE#Db!+WgSiI=R zO!*;o<9dY-15iORnNstoWQP`VMoY2*X4mIj>&o;Ud`?fJ;N>z~dk1m~4($J&`}@Mb z3!sf*f9v0Rvufg(RmoW?wVT|rzKRnu1ye36pz&f~B~Aq&PIS-eR_hD@sF2lu8tfsh z88Cj1W_4raQ+LO5>-w@T;p%;Efy3wtPP(`7drm#K|JOaYWg#Zz(hf?h|7+<2ay5T1 zUB5Lfv|@M?tKj}9~?Hr+xrDJ zN2JM`Q1o=k8??$tOy?*f)i?HXBH_9373;u1f+ElMXc%!EI=GE6iD!#N%>ENz{~fX4 zZP8YLHU3UIJU(>UxH+-tTLhT`Hw5~$MLPUOX6}KgZe44Rihm)GEcK5Y1fHK-n?LlS zU0g%W^HSwGKXflZhPe7Gd%f6C#@!o&(sj4_TnWG4@`?vc{-%?fPp|MJ%6*{?%Ad5Z zQ|o!X2ViF6TchZk-D9qY%O#+vceaKp#JObmIVxyl*8ONXIN%)xdei`29BTTvyY2Vd z;;C03CE{O_>Mqmh-{^AQ!j|dJjf#K>bmni1!lqTuG~%|U3G#kT7$Me=xRl_RfG+o# z*c{p)oGl~e1gu4HfqsU>5oZ)gIby}=s1e#n-d)c*_Gn=Y6`nkBO(Ip1&>ft4t! zJ&2SQb{s@{M-KQ7KTO#feY7+3Ll>aXiJwqm4=(_mq#g=%Go1+LW6NPOVel^Fh#&@Y zftHfP%zgl!4JEerZ}_7FYhjUdS64XM>vO<7O?5~xcL(oaUSkAb4u==R)(k?l8s}H4 zsq7Va`CF#!Ta8jSMwjw-DfBytOG>EwgwNwFt^>6CHyd-$h@y?#c|9rw+FpRsvQnT| zVf>xLOU!+Q{B@gAY0SFHK=t%=qu?_Efu0PuN@|kzTE-r?G5DPKd$zQ8o&|GA<8G*i zDwB7qaT^i=XXHWBLs2O?TPeUpD0`-aRoa|fZYp>^%N{68@A*tWOpY~OL#j6|$JGP3 zvCR`OLfgN<+nEoUVP`$Vhouerme;Qwq#hpKnD;>b?5#CF4Xvq(ZjAJT-#n+?v=-M` z;GI4dPTKn+WLD8>2z2PM;%ltCvM~JJ)Rj|Fg!XHF!Ox?UQT7s`n&`|;FN6^7^ z`{MFA-{Ld_Zc)-E^b(7m-gf;)n40+naliJzJTAeaZOh@>PNvTSZQMHU!hcEY<6hzO z2&=-*jW#R1%Q_>NwJSClQ-ugSOfNH%fpVTVEO91bwELbZ(zp9_`h(T7wSU|40@RwV z_xiVvNyD0_*P}0#PVMV40s*a2FQWxUf?l2O)t=Zj*r_BS_R6?}{g03WUVchliLyLT z068=5yEhAvk2yUk5AV#<#hDx^fhF7&ktB_-wue12*TYr0Mil6Q;g^lD<&L2juKpWA zW{T0^LeB2WncRDyUyo@`QV5m1!%tKY$B37{PPAZthN!_hj(Awh#p&?m?6P7R6=Mg~ z(gYkrK@;~bTDB0Po$}vGZeV7aPHt;n_>+`8qy@f-7Zf0(q}<>GcNIxtGHC?++0MK~ zh%^{Fk>0dYwFGIi<&B8tp%Qy~oJcPiJDI<~Ubd`huK!mG>WQD$ieiUnbEBrNbwkT- zQQ@2NRb|ejuVUTfzc-JTbo@L5b7!8X{b$pb=)(zoK-m5L9+@-HqdJ6HXmeG$MN zJJz5;tGl;L&24iFX2#>cij)6Czs;n9bN1c#sS%FUPty3F1ktrH0oQ5? zKuo^jQmL(*v1E4i5y(t|m*zY}?>py_I?CR!p@mO{mv;nQm`FpiEEm3HyS9baI{-E6 z>x{fOVSi@3P|Bs(?B|0N`MP*vNA>yYOJE+Uv&L$ePgcP$P5sN;ED9#9FlHl_qDy(4 z-Y`0|pZcxh;m_eaxpqv-w!AgB;m!eIG}1fVmhqOne&zyt{0F8Re__w^s(1J>Z!*C2 z3aY;kC3vHYVG|U{WyG6hP1GWk@pI6{mzlG*j|XPUP=w4U)uLIJ-3Khw^!p!dYNJ^F z){e8v+5OqzU8!y<)+YnmowSLk;+cHQ{fEsKzPA#!*AEm)UB+*_wmd4x0fL{1mn=X# z>Y-&E7?%Fz&9Eph4{l$B{R@yPYy4QewK)hE^^nM^`wg?rzZ`AK-t$#s?_DkOL86xR z&g{kM%I|Qhbwk11oweq5(Rnf$hVehMO%j~Cu#+$YOQ>{*N2aKoawb#ZaK=DtSr)}! zi!m>ig0GTt`ATkl2h?vu&}~GxR^$ZkE(_v5-uDMMO?_R+gu*q}YpN0ke;h5V8hm@} zR>vN2-TwWFqcm1-J^G5~Z;Y`FY_6cm-FQpyAu@pYxFVCII+_vkA=O@(END&FraY$> zK7zguoa4XK$j9X{Lc=&VHoYim>Vn!3qEabHuzeZN10;`q*}GnNiJQyaC^^?SLBsbV z=98brw}{qvL3UC8TKH!_!%zs535rz);s_Y(hsP^8H^_fzI)}FQ4L3Gfpqt3eP2InW z-1_KWpYtV5@=Oda&0d##?SO^!%FN%$+9p8*2h)1;H2B`ly<-N_XtJ0hu?@2_R%R6mZYtZQT64Au5+n9L~%s!mLeR+E>H zo3^xRpY`pRFLd?-hHz=2N%)bY^{UffS10?1BFpS=$Eyn4bYMlX{NWC0eLd`oZXNUI zSHI;0&_~69*9$2Tn*CM0x8rHkD7Ds$b>T!90ClWSA>(u}pIK_oa(8?ilQnU4B|Vf$ zH!1#IXFtp}XwMOnciMf1B0}>B=%l`vkoJ#~XH^YsR@&ZkNd9^@+^TuBF@+6}AyaCudt7CFGp!a4+lwxq*Ce94zQ2 z=6;Bmvs?3+JgB8xa>i1RdClOQ;BWCMX zzuOL)d*%A&cnrv2${Y-D%4(Qt)347KfPh10nnp?TUY)Rjro_T-uy5isnzs8uFy`av z)(F+|Z>*D(9uYz?{j?f2q8|rso^2KI?l_<1YQYPR>W;~uF{-0D$3IP$9Ee;T0)09P zqF1gqCPz64IAKAcVx3=urr)9&=!mMddb!TN6m75uIB`D?y1DZ8Afbaltsq!L@$R^6 z)xoleCN-X&A25e8+@EC^?uKFEK6-V|B-gE=`enEAFQhPRqM%uiCG#g6I{pc?Z6;v! zFniMzebsFGmh%XThIQRV*cKk%m^J^noI&TBTWMpHdki$Kx-B7$ z$aTHW)haaSSwv)taYy6aFtN=VR%U>O#z;9?KeS>7wWP1tV3YEv!W}{W@RY;gYj+ra6WDD4dFs^{2CVXwmYBkUMp+D zTzIcJiT@#`eXQRsrCLjCFcR3EkAsOw>Q;pKRdEf9e12=SdztVXqWhYne9*%bbJS}b z`0_pMn!4?CP0f;dx;%~Rk%D+D2^6bTIj^LB!I(X%`Rf}wxaZL=j?J=YFINHk*=zXh zhNd&O^M(7!U@v;nz0l-yHQD}dOkLx&7HuJ;*}Nvkh2{EkSp^ zsyjQ;e%|L?oFsV(^4zqfqjNwZE`YpZg3&EKv~Ji(%@i7l{tq&&1#I*F5AaynyG@>t z%G*$+jKy2p^?XRiQ-oXTk#;#{c=xWX-8D0dN4}wvHsU{dQtD1VHe}Wb;P}A3cOoe; zvyP1rAhL}9-h4l3n`|$)AstNx^Ew~9W|yiRkj2EumBCqBhDhJpfEqIELIi>uOW?Rr z8Cyrj2c;6p#TR{dgU_*Gh&;wS)B2;e-xnQ4OpI)rfFuuuwDQ|d|IePl#AHhTnNA3{ zCB_nRe-EtwUvx6?zuC!nsXL%N^R10y)x?Bi;PKA~{Uch=j?UsSNI!TgYd2e-DY-X& zudY}u>HmQ|%h2wOzcfar#?Um}4QH(R?u<~*Wv?B-iYhzuJcx)0)nVzIUBnYN!jAko zF?+`w3_ODeGYJ|%6HN1#jHlr2y>dX3Bx9AHdGAP~XuDOR*+|d;jSVRZ>cuQ8b?Ntn zUdWjtU3@5CDC~ZDwkvdkxaEq^%rmY#S(0xA#0lg*%cno6Gpi1^J35)@< z#m)F)uV6L$e3JJ?Z$SHQUXT9{>u8UEXei~AZjZTaGT=A+Y+2U&gz*S;yOSxTdF4cq+(>>;+Uy9=F?j!pPlE`mp6IT5xf+(cepVlNbip;W-%7BUVr4X zL$6|usu3$D-dk_%*{y-Ul86>7e7a%hTh42LBHQP}tR7@3)7IR^X_$#Ug;?u|oKv4B z>8~NiKoNXKaE{|k#AVwbGpL7}TEP&gNMdqH-%iH*YhZ0yy_xAanq$q$M7&#fVDJM= zXNg@nbz@|EpEqE@K&7t`$=&Q>grA0W^TkLwn>+@MAM~oYGhs(!8(9vb1E21i3y)?DwyeidK7NKJc-7JMQNXEBanl+2Fe3ZWX}c}Ho62o; zs&tBRjwEUNpurOOuDNK~U;o|kiAGMkY)Dbx(*d+$jBXzlcD|H6+-o3dAKt!k8sZlb zLW}iK&*zJ&V*a<}0&r6>*sfj3}__NK$uV`5ZP%KWMfU|8@1Ob5t_rjd(P6 zYu2B{`hYZWQ6Tg*tWznpG)AXtQLxMybUvesJc+jHm7H^jAA9IJdz42F1sZbY=Hk+w zI|hOJlhu7M#YIQ1GA}A73Z463QEwkO{t_?{hy#(p)3mKw0U~e13J?3;XQk>v+U1Ts zA9?Krl>E!>*n!5^@+Jibv~us=GAvjR_#MHe-Qt<)6ytAnh>llR*!lWTE>}^X)mHD2 zGyWz1pn&_+a3>Er;9}ePG^$;Ps5}8@at&Mm8Vrm=XYV?__##Qm>E2+c(T*Bf_-w@! z9^L1Js5&Q#mqc{NdFPbnzR2O`$&vR4Q{XBW@8(-n0s9GzOaJ@?t11cJAjBZjR%XFr z4I_z~kV1>vu>F{07lj4;@Q@tUK%n$ijO$=M5`J>#-3QSfp_0sM8PDI~6sFL|#W^eU9ESG{i#3BGRffHgNcOtq*%p(>Mk z6G{&IkV!Dbx%fDlC_vH^uxv+zSWOw%4sOZdtdyQ4L<*NIU&x$53e`s$h_I5$-y7-( zP-EBM7;GQ6yuw_VfLF0j7)&D7ETIMwsAQFTPOtE}`4a@w<}k5^K7Zofl&s( zSIdV4ej+8BoPSW+KWlVa5ur`33K+)J+t?eapKSTM+Wj6$3kkj<=8iFVd(?RX6}E8b zQb4KZ>gnQ2(Pyviq2r0h4SA~uZ>H$Ld)$Q?*4k!D-F-O0yYUotz;Nz~57aITfOc-& zk4e%`arV88&1>sJab_h)w%dE|aWpe02;dr-vKuGg(;Q#c@w$^{*Ln#21ii84G2#5w zh2Dp=L_yYFn+oh)weH#t%@R;ncuqE()$RRM(o0~tj06}!ybNAXr!`1 z-vbY_Kv_Yq;vrN=!?1N=)Bd!VpQBo1o?gO@H71wnR{scbeU%hym!P&3du2I&wYx-D z>T@T|1GIU92xt$b1C%y7?=aGMppG^X!RexkE=T(IP%VmE0t&XaKQmV&g*5#{ooTCXHPll3Y_M7Xc{?R43*BKr$&Kb~tHdxSXERz2uC%kLZoCh_LCgyihBWh_wiVOm7n zC^BW>8pwYMy40IqWVN=6mM?xy_4PZtCE`vXUGXk_s+hDt~BNg&Bz`a8+BaflAa{Kf++Y!0HRh>0aN5 zw4Lf5-JhwOYM@fq+J15o)~`vzqO?OefTHY=E5F_r71L!K^%v-Gfis=H%7q54lhAqc zhGH`qe6E$yy1SnwiMcDTcn|l*r%_z!ggdm$le3pVf=S5Pk@#BGB=(vJ6<1X$sK4<4 zAhX*(mTy@-E&RI**Ik`&zsq@T*)9F`j^sLj`lNeZf#3?=5YE+^;vj)fdt=O-yYs-@ z;!&e&mjz*PJ}KpQz*}tbi?DM>is?1*SRw;!eqSht9FOw;K|6;o+nB`^E%6GkGny`} z1=^{hFFk)cl{r;??V?daVh)VGJ9MH7AcuWA@SGB9&JjiH@Tl_@)z_7_rD(4wFkbb9 zPfByOg^qnOWGc`pu^@jJ%y2y`OMkp{iVp5aB+4LAynR{KT)UkR_Vr!hUDBzrx=|ce z>_=ca%>xLuLsgpPrXl*|j+M>$HB3L^5GJ-_3iaQnHqH%pYqxB>YUi?FqSwp1$x{dg zgL=+(rpfuL9oZFW?vUm$o$>*FulLD&8Gch ziMl-jv5bD%?%OZNTFRL68I&u31_3!N+W#@z&{u0f&c_&3*D&m z@7ASx*BvhPgyOU>cKBJz^!Pd;KN7pf>P0q<;8V@z_bd#G7Cy#O{gGBS!Hzkv){nkQ z_Z&meVe|1uLI^4nDPK@pCp#t}9`h{XrtH+Qd6@mDg@touEdwB6k!uh+%aoI{acXqf zn2Xg{MEybXUM_>=cOhr^3Jq|NF!Iz|{R)YL#|%1p(LY|o1IjZ&uQ*RrQ(-~V>vzr} zT1X=k1Y9boB;&L&AuKd+!>0XZ&?$nCu;RsW`mCjKfKI4gitgT)SzkM{xSYBXX=^VI z{32A}^O7hs0K~zwMq#SJluY!oV9eh9eIoLOMkaK|aq;ydFC4;{w-lZGvQmu z%bMYLjE9#P5^PniM$RkKhj?kz+d*x@l1`3~e#Oi6Zdd)RcQSsiSAdCqm?6;Nh!7?R zy$0jV>xrg@!fD(n@VtEL&Equ1*3MixyHE^R(ynL$KnW4*s`OOeESGj@ce+CecnqjL$ED^;A)P1o3 z;`iv|)0~LSF+WzCwq04eBV3Bg7?k=IgIR_GrCAz*1)LKFaEU!G$(>@Vc3Y9c9~ESz z#^nfzGz6VpU?2VWZW&0hfVDq*2?o+hdl_0beOZwGCj>$?2J>RE!k1}J3>Ej8>j2kyaRJBpW!#q2z(drF@a-D6|w!^k(z=oymR=|go6|WK4 z>TmiTg?{t=We~5E5OI8leCq)+GkHd$bg#1Amkq?`m5M>2<>67%vzD6oP@qlTyHw={ zmcBSMPl3xa|WVQP@PHxdIVM@!r86USS{Q+-ttdjSl3yLVkZ8&P*3x+!XH zIK3)sZ3t&SMx6&_IbZO(-r#TXaLqX_#!F)AT%9BR@u8W7&<29S9Yb1WaH=<6Q^fbj zJ&V*CATFs@u$#P=&+{tDI<#q0`&ZN#Qnc}VV3V^FghGs(S2}!HYG_R=MB1NRcbQ5* zmlib{f0pom)^8)72w@q33$tH(Ip%O_mfjA_AXT<^<2aLC1({6TbBA#)ztm2)wqCtS+|FC)eQ7D9z1FIIbmiREH?D}s%ew3f5mU|l+Alc209rb6 z=W~?rBhTBVBYSB=TIiu7KKbI={>U*tf?rd7y3|-~Dj3)oNPfDTdVg5M-*FST)^JfV z{y%lqk$X+JrOY$P`@~oyWLj@hL03KP%;k1?eNyQ46wjZ!uP3y{QtZ8=>fvkqj#}HJ z^1xA$@G|RHz0-ycBzry56c!elHldmvc=Zh7G|#K8gNu=Y7IbcWtbp>nNTh#1YxdG( zql1SY1>N<#G5wTw?CxHGC}V%hPTg4tw;e!F%+Xww@&7Ohtra$HuJO`RY<=ZRNw=)N zbrtp4vB0{1??7)BZ{>cHAhZha>bLX3ts}rFQ3xc;`8w;8dNqPCW$Pgk4GI@QV2b+H zr@eRd^AdhHom_Y1l}Xpguz5pNkZ0AD*x}Kl!^c$5DS&}rU^hLRu2Lz;JFVSAe;>At zcedLjKCI)knMgxY28s|lKxh6Dva@SOs=jINPFg4nF=qT_ka2cPv&4tg=3A}?h15t~ zGL`(d**lM$l38q8VnI?0Tl;9?kpQ zVM^bDXN}rcYd@PEp<56VMe2S3Hq4*c>Cs`6TWe@-Zv1g z0!|R<>~L>NTr9|zAIVby%%7><7b#Y++?{t85-?LK8H`)>WS)!GCL{f~rE&8{GNsw5 zy!zS{vf^>Ed{=i>pel>Z;*7KjaWs^`Ppi2ixiAAIn{uKdgY^CBpA zw6NV(35zGHeZR1aylHk`=uw_Fhvno3-}{O_V(Wo;9$+N(koAGb4vNG66pY_vhH%Ihq6g(F`DUeA!syqk z)xnY>=IR?7mQ3b^zO+qgy*x_GMSq1Y;$hb8R;#3Jb1CEZ zcT;=JO*aX^8dv)}t{JklS2DVrHW1Q9V^?~!n%4~gX}c&1b|F3$x2@lYm~i$D_lp>( zKM)h|7)TUf3MEC^!!Lia6d%YDZScH%^9{dQ3{}1*6x5e^Szv(tpj6%1+!gO{_lti zmBUSRCumq#NJbVfxAS7u>>cGD<@!sjXQ2Jea@Qfb3 zVdfpE^j-ONZ4-%raLzey4%v)e2Ush;__e=!lH~D4OhM;X`q)1#|6Td7;}XAD=4j_b zEy#n+@6!rb4ktrckHodX5t{}_&X}M)ETxrD-^V5_t*aJodB;;DC zZ|FxDr3a0MjYZQ%Tv7Jl4(A&llV$B{#eB-4`IIZW={h&_jFGB^&qy#t(MF0LM(SEy zzp@Sln!-5)au+FHQQJx7W&9zpRD(5_81F5Nzptri_;8}7(pyIh$r~Z2#D1d=OJk?f z_qH6t7g@a8zYrmwa2mBnoYnSb;5DWA$3&?%ph!X2ng~PN%@tJQeZz_<4YBrn1>?=B zGycb(g?T~%LQSIte=GZCt&sKL)7?C*9za_ZE+QO7`kEEd#-e;5a*)0#wnoQlOK(B^ zk<0tXnAE=`#Ru5+X>lb?04&hTfbeYOVnr)9+ zND%Hm7F8BBXCaHpR;(K+w(#=@W2*6@fQR#Dx3B^ISPa61DYfx%8Rdaug8UPV_L3TJ z-*aPM3sP{dygol#f$6VF@f~Ct=1kOSyX!ntw8FeUSjCXHf#$eQ>-NbvgLlBBs$6EM zIy@=cS(0!Ixh!M ztf!_UgnbRc7^+_cAdR7UHuD7;ci2XeHH7J!^=*8JsR5w*dQ@nsKqh=GCQw4S}!^I($YEbx#I?j!n#BNGJfs8i95i?B4BmYrtGwHU2 zbWe+@iH7+6o73G7{ZqBdyLmub%~P5TQYTpb+rYL7xJ4)eZ_h5NUNMI>=##jj28wZJ zo6~2^lz0{w?C`^&PP9f4UlQi z@G!%fHJ1iux}dI8g0mLwN97vL)Q3qEYITAu@iID&)q6wsFR`a5QM^pUqeYB?Y__2; z2yOM7&!>56?vD@2SZNb3-Z1D$p!EB}twxfo$~JypA-lp^n)bI2YK)iGRhuKCwU7*r zMCxpPR}ESMJ)cjIwW_h=9|oxI)rm2bknK3!1r;c@@4+`jKmz_78hs-bq3LHVL5|0g^t!i`h;cF!8j6}=xm@Tf7CzV9o+HSB#q2Q_85fWHeW zY&AR=^as5r6Swbp09jwCvGzpph$J0AZ zd!7IHJJ$Y}PKi}jLQSU%5Yst4#Zahi z<+IvZ7UOmn9VpBbnf4Y>nbTzWo4Dm|Qk)`$Y@45jxTTP&cacg|*|PGE05eRTTgD9h z8^uuS2{W2*xHEg`W98bZz^QESU?SJ1t2<(PF+r$)}an%bBpVmPT=iDp(e znQ9;`~fOcU?^}ZwWaSulF&J_W2+;e#GRd?P> zHga;yn&|wjK38CV&7qHe9XMv*msjNMSEc1XQFoLUzLyp<=WCSCgx~_; zx`I2m@jq1(t1D<48T7U41k9jPfm`RZEiU3P<3~r}?Z)Ti;r4vI(#8)=aADurEnpS{932`gR)8;r(MftSYKT+#RKxJo^&o zox7$(;y5?<(zL?YZ-jP;=jAcl*HrA=&AW^d#y%1N*N^&^jqwCgPIFOLZfv=jXz|S2 zpg*{wTtBi9z?Cc)his-N!?iaKBH-!^QA~+%|&Ztb^~x2Vc9&o$L&lS3+wltpo8-Oiy_84Vpm+- zZs*K?%Tr!PVe|I5Av?IrwpQ(pyCqYLlBHIW@M5wc;MKg=lVe?`b%L8LNU38pEb@Wf zcZ6)8Kq~WOdL5BmF@Uu?FWiu6=Pw7AA*@v-?dvdi@KqABE_Uwk5l!J9l+2IV@p;Xs zJ?hgSQ*3F+XY`>AyeSX%-qf39apSKfkP>UuE1r zJ2+gI4-ym$EK+|KTOgi}7TuU^1iEsb)1C`wOIdxV-(c*NZJv9QV(FN^@$J!YVa=OI zB;hli=btG|+=)Ctex!W-g~Ab+!F}6zgWdC~sO!Nm$9H5a6Go{8FJ!1so&+4dco?m4 zz}4xv!`lOqf8YMoU$1MYab+{Y9aXV0?wsSlQh3LOhEyk8;zSy~sTG^T%Z+tF->^o{ z(%hypRb%lg2nK{?a28@3)!3t2YLd94Rct;FZ@D1_4+EP-31y}2C=%W|Qec#P#jYCt zYU!YWcw0a@Ew+ebmv&I@TwHHqXkAUn8;ll|H+66Tu%Yh5`i5GzTA}F3tNKIXv0EAnTe{(-zvb~Ol<=I2dL60TYgnQ5q$$*y)AvJTx5SisP>lwsB-F&j^Y zcEz_cy3Cz0kyrX*SUD6qxXf3j1JZu9Qq}%59?K?s)E&h<3vGWTB%-)qE|q(&zKJ?w zAH!#i)fBLoXpYYf%`h%_N_t4i4RQD|THh1DVpVptgw?k!@NhZlr&}>Denmoz@<#Y6 z!!p3k+d37y@aL_sfJ2y5#rE2t%2iY2J|&lvz>bN^=4uUetIbm5`&Z`A12XMF;b>2x z(#>QZ8khd&-%ets;fN|vjcX`0GpOj({_voW?6ir?uxZnY%46ExA0W6JHs1sEuf<1} z)Cg(UykzjRPSQHwEgwy{pTuBaS?Q9rlY3L05J?0XyD2OlXy-!_4WaMlH~GgJ>^6KM ziZjj)iSax}$}0*->>j_x#$_<-g_lxogsexi?cExV2d(yuIFg{i`*sA>LXrB};(zKA;nw@$F=l z%AJh`8VQg-jj1!gx`NsY`1SI+Pv0OE1y*g3DM1t=0{z*DR=s%DTKUUa=ofk2biM~Jf>O8CG~ARrN$F*}AH@GrS{UV1$Z`@^wL~IgX5YwFntbb}KaVE# z1DEaV7-!1lo?Bwy?smHD+wG;09x?_a56)s5Sj%JD5lfq0&sNTl@hGi%zH>CcT@=#l zLq`~FIV@l0-1s8CAgkGIxpsKY;L_x%cFxt~w1b~)YH{9e83s9~gkf>gODOH#WOXkJ zcU|F-|GDOPXQEj*hHe87ZdfgC)-s_2@qf*{3zE2P5X!P2EaUMB8anQ)CUJvw6gGK2 zyqSDH6KJm?C&j$}DM7`dZ`9qc!BJ6m^MjgBPHEu-nUf$T&^ zoLar3(Q9IJ^&sPil>!1$?H)0j6X8=ut%46VwQiqJtCN?Grk7qN34qhl>P{pesO)Y* zT}???MFCs)3b&~I#Wpt=p%Z1{P7_w7IDXw<*eyZ2e9qs|N%qiBWlDDEf*|s7&6-{z z_b6M<2FYo@<8}Vw;|OL@;{auAR%oImJso9h&f8)8mpSjoSK(lFJ%LNr2kUN$iBF{x zZg+ZSI-94ae3c*!;3L|K-&E8CUyk?W31u%91VPdmg#}?skFfl?LJ+Cjf(kSr&2@j0 zJ?TlLKohL({C7{F1ZgKbe$rp*B)4cuOQ??1q~3P2t=`dTa#q={Ll*F|YjbpM%iyg- z+jyy1Ow1^R#g5fEPLxJoD6KhsDbr^3g+QO^xvlvrjqkDHKY?`<=&8v_OG(oHx#t%* zlmx&ydE+bBRZsJ(kJ;`Ms~K49cWWQ1%4`Ks&xupzYq{E7_c2Zp3(EcAhT7m?Fq+ni zq9#e1_8H65cj9}RfmmX>TC&t z>2n*jz_ha?G~iW+238*#bcDai{-XGqE7JqkUYbE= z?^EfhaO1BZhdW2`ye>8OsWD1UEaj-0ABj&h_{dEi&Mf{qm_8Dquw`=xHq)H zrEhWrd*m7CM4I9cAN1pRoQDH7=Sv=An>$`1{no99+Wc%9O}!#qSCBwjylU6>QhrIC zs%0brxPQsk@{I7)C6B_xMg8gIAg?Ox44%UtR@<5j5*`=qr{(u5#0X+_=j|U`AzWe- zO!n8?z5ykwkD4RRi{0BmYX9+F+rL$D-DJ%zMFq;gYu9vWCpBedHPnrAMY&!I>6l?FMd{>?N4CSKQhTN0Z!S|u^ z;cgQNYaO4op6CLnc`C*ClX(2Kn2R%4IJM7IIPfr%<0bb!M~hxS+$Nlrc$_uwIU_ZI zxU>DfCLP}NEvb)a&&K5+gcbJGFBuf=K)FdyXC}T!y6u7%WpU;;Jft0^v9S1qxbinI zr;t}w#;7jhrOCtPFF$7lHMVRr4q;baB95fQY-1;MR)P+7SA9Y=hKAi1-D-SD?^BG2 zJ{RB*;iW$E-s$7}oMm0ylD}{BgO-0e@K6Tz#foEgb02ZV2PyYXNc(YR@5(EJaHa~sgNjI4*PmAZo6L$_hqpUfV_%B z_-7nqyw%d6AH5LEZipBh*4M5kzduWE^(GP#`Ekl!hIWI5EOi&1d*wX9P6 zjtN<^_bj*7{QJkD;D#)|Pf@Yl{U27d=jrDawUjUkTcJ_LlM_2(X1CY}!cg{s1-b#| z#n5~>G;Y&4+?wj*rfMO1qz`bbd8M5DqDpLn+(f@kD2Iwc_@z%MX`u>soZS;~!JAJ^mG%0(-KsZpG)RhLAJ4tp1M?>~@| z-eO-x@M8=w*6plnEgGQI=OehNs$|wL z9iPlR9Hhv#hD(zD{oWWy^dqmBhj-C^`n7jW0Hxy@d(0KT*}q> zCzcyGrU<%NBYUs9uTHh=YP6UqNx{_H8kJ)c@guETWi4FhW~Z~2n@2T!=Ad%4aB)+b z?U+S;Bw!*CkjYRGqd3N6urC%1uu2<8QdGY=W+-Cn?#3A_&@y$?;#{?inq`dBJfKFU zx~iBucVLMDW`G}J@n5k44ORhZC_ynRg)>fBQ65ECj2mj?rKasC*(1ccTge5iTGNPB z0bXvY+&v;Ac|8G%D<$eJcTzMt*FUSZx+f|DG9Y3j!e}$avXX7|A=tDIH<>1$#aG-D zVA?g{ZQ?DT)qa^02p*mYA)9=PUQVBq%c25VD>3TV>fGz*Of^KxFZcZ%E$x`#2>HHN zNwe(U=BqOn^EI6zd5?vCt|(;1J?9UopvI1VP6|SOnsR47(~q_YS7cToXC%-lUcp;;$uX;8$ zDjqH?qptjgP(^ zI(6>ntlEzHo*D~;U&PR5z~88@=7v%A4?Fl$^_l!1Dbn|*=nvHW?RjsLwj_E)Z?!kY zFBT*oyF1BAaqU~>%MbU_cV9FAOgA~G@@VVs@Y>_h(v7lI_6n=w&?Q{lOdVWAF_&n__g3^aVtRV}^#D`!rCQ76XMST8r-Kbt=(S_}zwmZm4AI=W{&^B3CU8!i*Q!*vdM<5(Bbr9tP(z3@ zD;$prjqdBjaF^}rxB>Xy;jNnxUJONWj{N9fIe#SWDd{X8rphBOhfM@Y6QCRyCxr}byEjZGcc$~_ zx&w8o1wm7t@wb$#oEc4;MjO=e_bY!-{vW#DGpwmRYWp24V}a-(0wQD-6_pMuQj$31 zC@RfL5g~L$NBnQC`C$uNJ0z{LK-0?Ia}vB=Y8LE zo%4}=;mXe5|Gn0|e)qjvf=g;ladCcI0=Y?%zz>s|GPKIr&3g~3QXa$JZ?~q_UHaK2 zEZzfqN$&)g6W%X}$8vJj``iwXBv=o3*w*9B58GD2#di<6gvIRR33dx$oosXKACcqP zyd#}WHlUj^o|>}O5nGNrUrOJBKhM$n!FwKC7P#>kr?NkLh$=P@p8#96Urp{7;Afxo ztA(pQm_aii%YaJSeso35AGqE=cam`zY-2xqyH{}>W}?9>p*bhj>Xq$$?nnFc#qhOZ z6A}&c*yVZs6B5dDPi_m4Y5(c50DeX!6QV6zw}O{0i(oc(cjS~{VeW#^wQKhncqT{= zAzSs9$c{^a+l3r=8wn8oHXu&Cg>-c6YhtV##UXS#wm85qMr6#BD*VhrIHqcvC)sDJ z#3+c-c6@nTzapOcOhIQd<#9zlI7g>0UrM<%+zkFM(5gOMSXZ4oW!2GB##Qt05Fip> z-P6lL>}><#9Df@;AdMIwZ=-msexrJBljsFkkmsaJ-lTMq?#6)Al@X>GlBeX;~mHT+IbWHD89*kdNg;uqhlrLtjOL_I+Z-o2Y8p8qtzt2I=4wd=7!RC z$hCCr6ru9#H>f;UzE9dpZa&`pBk_W~2cE_a>(=r6jAMK9JoK`b%u4Un4;t>8G^vDa zOX|6sPsk><1qu&l6~83`?uRa4A|x-y+!vT|0Pd!S=bBV{dT^*zf1-lwq-4sosYe%fl+ zIumBpN6e(cE5!0W)LA**GXMRKKBC&6epa9m;L+QM0V9pYS)MzM-1sW~1!hvJ*()0W z%LtPxw0|n|tHnE1-b*h3*)E%=CDX-`3dnPy7JW@>Ctu6%QJFz6ZFmn{+MeQcKJT{~ zNY-j9c$=&~XZB;D%ai=3 zME5K$?6Ww*7_1Z-WrG1!F03d zHaN%Hw>YfK`y=+jyhzc%k1yAQt4*Y!G>$;=Ru+m7_2QiShJ^?qo*OuT%lF}tnsz|7 zW!f?^sgTydasP^67W_dx*B9^FD2+ckC3{79+#;Bk6zPo1sS$+czz2Zdm4^A^T8Sw^ zVvC+3koBb#Od~)bqOZ9fLdH0tr$knJV4`O$WL3>3hBr&W9qWQN#g#Y{stu*1OgJ3I zrWSIpKmnUR=?tl$o4;|If={Cu=l>n+>K?J$H2G4YkjY!G&$mHgkt+o@G}QdH-_JDP zY1`b%@QT}>iF&u};hEy66*%#h(S_O-<_@_OYh`+(W6`R`qgMyMZJ77AZzoM?M(GJl z=^GurIUx&RAjfBTgm~+>koxG=lr$aO&`psTaJ%_EoWYyQXtN&ZS$=VAJV9L7b5jYD zw!DtQOWjq$Bq5y@dE2$^7s%@~iJYq4>=Bb8F#E%b@O3EPH+Xh^T>Q}e?p3w&hf}eu zr@^VFu^*%DZQlh~-YQQuWY}{KDIoX{Q+YIZr=8sI=<1ZFZ?cRqJXq*)#~c~ELI1Vb zPTLP26``N!Pyy5MZ9jF-ndo4#rr&ey0J6_&Zii{#MDFOohsL4ByW1x+69fKP35ZHn z6OGY=U&m~qaI62QdS|3hhYMCc-4ix3x_1sSx;~HoAt%jTu$t{m+YQ}F$OGmO1NXza zxki&|$MX&w@KT54HYE1k+&UNb-3&<05|4b2EOLhS6iuqpZVrEZ)^=B$9MDdsq?L8> z4%PbSgUiXYK<_{Jr5X=Iu0lBP0yW5OIr>6z%(UQUf>`XAEl3)I4n_~@tT2}cA;mW; zqg7Sg8cT7m@v$PDTV&jKGeuq%77ZAVhC#cI_YpliQ@P1?t74xnR5oxr1wSm&)l$>U znqVZN!<^fEv4ObTLFDKaK;GkW2s8PZPn4;eFS(M{{4ET@{q@(f>{n*@v+m8}#hiD* zU@xR+7mcA24ny=YH4|5ld1N6U5ocA!-GYxy?@+6E33M2fK;P4n=5(wM9V9&}^#jB) z$6^wjFdLZSKubVhDIu&b3kfXV!$XSNtkaaFQ#pjVt!iP}z!4}(9s7f)m5E(!*Bv;C zUBOJ`ffvVpaovDCh826rO<2S_D{sCY4gPk<@*Y3JpJF>h@kWF>p5I)pDjzj-mVb`l zOMR1aei2TIKi|UM7mN&h9EDkWT3Ee!lme0M!GbW#?7n#o00Hx!whT}We;wYBvyx7+ zUPq7Oqhfkqg)|>)UIN^6-1)aPN?7Mw6&CWa)1%usb7IczW?2>Vtf7qF-Onx)B%K%S zo7e3}V1oBq=}0L|w{)5Kb&qKO`0CH+o(n$r>U~UJsXyPozvX~5ra~+J#TVs|+0($; zZ?rP|ez2ZdZY0{%h}RuZlmA$_MyBf!5Oby3ht z$I9!GkF+l9k6#Fq;0Mge4Ab7ONUmAo5HKGgkXhWBU*Qn#5WV8FbWNX2jHphHvq5M4 z?^>*m_QjXpcN(}Tiv6rRqphzB^p16~(wnlU{gyW9|1rBx4S1>NtZ>Ala|=N_`B6A) z4SD;~6zrd)HM<4IDx02?+pa=XhYP8KCHf(ob)^S;xX!Cnd9*8W+n@&yBo3V10Fe+q zf^n}Yyu-?q@EZ69&D}qz$HkByZc1T-$m+{d!WD3Gyg#NIzM=bM2^T0!=G6(?qrA_M>)aJ<-RybiA`jR12j1xs9jK8zI4V}2-Ez!HCZynxwz#nH%j>`J zGd+4b*sefXEpR2IdM*_G|NQf&!J@BU{@35TDJEat3~NqYfacIGGh?{F5p!r|2tBo> zdFn1xgbw3}${+b;dqWi-X|?Vc#p=ibXw%^ zBgoUiiwp-q4B|X2YRk>2? z8=_JtO0c$^xdu%Z*^`&^{fokL`!fl9H+MrVeV}+t=Iw5D6v|!ZABvel1V^G)=D^LXB*uc<4+KRBluLGb8V>j(q z?m-*PVcY^{QKRnrDZUx7w2`lJ0&)g;IQqN`7ZKcU#G|}UGm!{BKH|j5l&&6k2Bn_z4K2|Q>aLh~y`^x~b z&EGwsBj=vs_g$UPGpmZ{la#j^E2X%8?X{MDe5hkd=Dg(3`GVOPmXKFPvMLmCj?61= zUQygirtib@yvba19cdyb{>w!$oP6W3bCDljOt(6q%b{*JIq zbpYBV>>D5g6Kk(y7qQ1BsZVPpE8p|JhnFkPfKeNrhJ|MA^>2ewc?aj3h_V&_51@g% z>FSL@3(v&EeTcAx$%^B$<-oRu@?or>=2`3KvH>f}xZ@mU_=gwg9L0C|P+U+{Z>aizI}1iP3w?ev&;pGuM zwT7ak+hUxcxRtVaN5x*xyyTHRyJB-l{d*rt_c`IPCs1H&CS31>|CEK<>sEW>sLdF* zEDW6h3pESxMpKUj|<0OT~*O0 zo~Ch+I@~J~V$Z6^Sc3HmH!TI-i)v;Ugo3@E`2?nYH?q8}dT$)2^yCaLjPcPUO>aH{{cIL*m@gQwK+4q)csA> zh4_#eXgutVu4S_$?4St}aM|Tfd6%-KI`YzO{plsa*{#ej55@G4@#(>2fhn|EtN4W< zK5eLNHmGnq=1rDYbei?w=&@k<#9z3%*}3^=sy2fJ`j@|wKEJ4R8=YA=+J?Rkcr?QY zX+w}Jt!vp+ZO=x^7+y``L6kgRU%v{`K?WGu(YUg^ESV}`w?ZcW!2+`Z<5w;2ZW)k< zG?-v?B+{|{EENL$aTEa{@t;7N_ckx9A~N7Hf#(RZ^8Kt4;tHPso}jV(fxJAyZy22i zpQLkeF~W1T&po@$QvM#`yE?!PiJ}FbsDLe7`lCbEIy4$DdInrQhpOKPWBD7NMKwgZ zHpsmV>~VEm?`fT8`GFp(q*@(oHB~tO#|2oYHqKh~c%&zz(eEuZm!7Mi|7MTnU-$`8 zA%EU5_R>miX1rcA&^N-0&btZ!MtX%@K~1`fURmFYjfczY0skT%9PmZ2=px?IEyr7Q zU_$rAew3VIRsi%?H1?e%Viz!T#|JlkOP4L}&wPbCB_YXYB3a)R;_c8}Z9P+F_`(M8 z#J?dg;lFeUIXk*NLNqZRW5}rTjYH2mz2dVCR|ohHRk0SB8$+*I3P?0~nmcrXZ>##v z^SHm)0<1MG2=b$H5%HwPH(>E3D;E@eGOZkR4pwWiQZ5a==J15wA-0l**gk$Y&Yl&G zlqK-DKw&{Tk;%%{1VDjqG?ds+8@qE27xxc(m-h%iGX5#}65Aj5Z{X{mRF|nBOCRb| z!Uba^Z;KM|wol3KV)xu|g_ol&3=eB;{vcbwe;!2Y*5-l7cAAr3!ABkOD(H-E( zSQ-NzhO+sHKBTmp-!H-#yK#d1H#PQa8>}?&2Sk6z1Ku+lM+Nf^*e?es1O%xwu#!y^ z)A=)C9m!82FZ-+r(xT)I{x~n{uKLn9+t06HjsKm`szh|&)_p`Xz8+X z>*IMk#cH1D@#3im30TLwlH2^*M3|Ly5MpJ4X*>HSLemZ^Z-f*ed>%>K-0sD}Or)gb zUR#MvX5>N4Dh538r=5_GfCa&{Q;~nmf;MY@2r4)>kTzgwD`%;TzM{TX%S$cX!ag92 zXiYB{@U^*FTJk#ULhDyBmJ3dv#J|t*S!^wVXUn8)xW7d8QN&th!T!LB{cYfmVRFQ$ z$iI7ra3MN=eYTMLQ5I_h4*TcID}I`T+f!M00q08g=a*8?XasPHYPAxUE&hId;#q)? zgRALG2+zB@-hOxBtq-+(*Q((+J4W+z5@Qb;gJV?1Bam9N8)eE=cD?a zIEZpT-_jH2!9D45p>+g@R*QS#nBg@2Q*QNn*(yWvj|Hm?VGcO3Y(sysinPj(mrNB# z!EQ`cfY~R0*1)%_a7EaKMBrIoLSyuQqxuM|nEwTYG#L8)q!FlVcY%xb2Ir_Tso^z| zca&AEDp-x4#wRmK6vxnNrr{2@PoKYkC*BKOGM@7E&nkM2TqR_v)BbNDW(LkE zHtJ~jC(*#PC9dZg!Ib`ZTo>q7))>Zy-4=8zy<)fTtDhY*8PB8jF6J~NvK2Oh-?=M~ zGqAf~82drX$&;Gjoz>Nly%!&?K57`IIaY9oG-W!Jtut>QC;s-zbVIQadeEB6U3gMj zz7uHwMYNoZLUyHaTFS(FZAt6GImQmR+MmWYa<7tsAz?L+76x=HlRRXi8?sLy7t$50 zL}nQ#CGX>n!?7^Gj!LRnG<=OJSTG(Udz}tD{nwnkabqJcoA=vEudx(RU0+@}8jkHv zv56u&-=#rkoeo=jsJ!@M}YWgC<_v5V_mwst&#~jipxiU@Vn&bQ1 zy3|}k3}qii+Ub?uRw4Nll#UqPNtsx8fDB;oax={04KR~~!K`irD;n?SE^1sXnrrC( zIB!)08fDLNKwwWcjG*tItcC5ISQYY@ITrf4;p|8j&ONgJc(6DWJtaY%YcxD=2~#cQ zk|rKb2<#ar&8OYsBGI2|&fH8yxUv@N915rFFJ}r^N!8l9!fk66hEH^BtXTnq`niO{IT4^pD6Kga$`C5 zhMbklE}0cpqe%A8;PYbtf<`w1i~owoj4Br^vyd%QIq3_r*cEa|9qpzaT5(i;K}578 z*p3do_RqKVi(4*0IjE8s9R?6`So-Tx#G{Fu9i9qZO!YqeC*WcqNh^>NDhF|_|DLl# z8=4%_fX;}{E56d0GZF&-u+!kZfgi|ft#*8I%h`bpt-Fq%YIS(m+LX*qagOOgei+ui z7HakNyGm8;zCl`(v>JxvbJCYc_o9-CN!;y zMV-lPo+l2NwM->N)R6GCg%T{dVqW|Fw2hNwqw^22&`#jX;j`_G!~G{&^O-W2l?l?= zt@#f0;`&E<+s3f^C-RlS3-O?+2sbO+Dgx?&@Rg^iCMR_J*`5ashh`lNClKM!E|Q-0 z@1Ec*tGZ<^T!?3uVVoA(17=dLWB`xo#+nt9n%SBxb%dP3PaOK9gA~!`TUa_;K886> z;crOeSZ_3|1d%?0>00?W#zc}xWaK~!*mw%pd(AmUm_r#*F$MMoH$kO)#HDYQ6rhBj*rSqr z>EoQvMSfF>+Q8xpwG`Oft#OR%J5m2MIqUFvEunqBI9o-#Q~2h6Glmjz#iyR*RUK)S+ z%YTql_d{^rea&-MSVV*-Sr7oeeDLooHzlMxPyNTV=uKt0Y5Q5HiPp3#Kxi2u4Krsf zZl)7cPp++D{`(Ap1jK(|HI2K>@n4_|UL(mP=}7soL3lzy#Am|P>ICH@1_e z>Zh31p~2DT?EXvQ&A!oYpOcmJ6Ti?#U8=b-KhAw+dTV8lceAr*sX=c&Vx(<6VxT7* zMV1b*4o~L68exXtwMJWsXO^Lee%kr|JKJ=p<1t3atsW!Vy*famHPBLkew!1 z%w1z1ZYUjOjx~>7w3sR7TO-iiP`L$G`tG?t5wKnXF>D89*zMd1sqa>wYd?e$2c>Pk zomR;7GK=&Od&_CsDcI5R5JA>-3O`1xnoqvOtI^ux`;c-F+KD#|YNX3L;kr#3x_)MPAODZ=ai z)wf#7R~DMR`obL}n(K~}EG{Y)L^n(HS;6)@9L^Vr;!QA%=%$Q`c<+_vBH|ffaVWEZ zNnUVc&0l9)2{i24?t#qweqstx<~$>+kzGNrAdy7lq`r9pG)Ogf(d`^*k1TQGk!%D7 zoH1P8>{Qm5iMfKV#%P`$Y}JK7#?I-RU5$I?B=36#niC23PR^-;PH27``D6D<`6xo3 z$0yQO>sQ+A?EP>g`AJUvrIlzX<;1*`#T&stobo@G+pi`dg&%-Vz=xr2-AxE7Y2v$SDC$^lJS-_v@j&H)Jk&MpPVu*L6f>0R~!pKN1FFOpax3ALZoCZ6MfI}080^=s`U-0aU7UF@)E>ka|ZWjGvbcPCnSK{&{ zKX&YVG-mTx_J9#@BgN_}xcZ#0W>B>TbD%cmcwvHP_FGBT+n1hQut9AnQi?{x*V52$ zOFzV&dD+{Gy0??P%GOCn*k}D}3|rN7^ql*fW?lQwHV*b@>u?4GAEIv6Na}K4Xbn`l z-u5E`y>$TlW0sb7o+i1N^3TXIuaFbn#cFDhUOx5XkY~;?C>b#6@BFfEmGs^1yfzg2 zn&jOR`#4IJ797AS3$_Srj7yXn=Le70&f+*%_ffTR$b07GLxq0M6r1is>Y!EMn_Fnx z?q5`6rdrtE6CGW6@G_My!yjc#nP`0S_v!}5++TY-`?2AECD+w%lD!9ueDPUj*R{iB zYX0!HRi`xF?8I230YTQ4-A=|@CZ;%37>KG6ee@|R)XXG2MAKdYH+d(8rFXrX!b?l`j9w|u_WXAcxj?;R?htisZrxX8+Fw%#Khfb{rHSs?CSao0 zEK8iri(h5BjT$v$1GNkWdyQ`-2+rU8Lv2eSgFD5V6XeVfTJeclkb|vs=`oV!RZ4oE zq%=9=rqDR9%!bS(7@VbreTZy$oFPpLu*rsDCpR^~H&bI@?vPe`V_Z_0YgBd*c;1#= zhy~N!BG%W^znD{k)pqVqoWCvwX6wC;cJy+de|6GK7j^e{H;*tpEyV4-jxl$<*=4x= zoV6wS*d1<*)PEZfVY0BFwk^gR-1 z6sz`{{(s-b(`BGFG)bq$@gi>QFN=MvpLbr~>K}*I#!tp*Z^`>Zq9|o%qf%V^!7|K& zNZe^R6n{+0r@hGKov>)=QH4w*S4K;6o+LE(-xFDM*DB3BSk#D>hDJkdp$%IBx=ZKn zBIgERFNR|S=!|m^(hKM@RLddhEE0RXnS04>50rS?$32AOEUJr+>$q`l2$;@g#mT;J z{+IH*eR|r~b*|*dEr!=bb)2?-f|dL;OVWFI@X{2@2DkiXU0dcYo5cP`#Bv^A9e=tP zCB49XAbLe|^Nw^;u(vVvX#_TAg%8i5Vgu1)9ej6yhUV&+bw5v@>Gs!d3T}|-u@F^> z-ImXbx#@Shr#qK~Z2I0T{zso2F06FG8Z^_NfV<+MLM9@`Uz1b(~a$HpArFZ-6i%Ty~v~2K45dk zyBp5!R%PP&>0Q_VZUN5!op-%xdYJ?*PKD%aMa{d7muXY_Gv>Ty^UBu%hBRUK}0qYG1r zpP}r!HfJ!O5;4}0g1@J$_J^r`qRSKZc8AiY{Q&)Tm~~2{9l5XE!TCA80;Lk~4@dlCjO6EgS(2#2OQB$9$PLXt##~pVYtmHf4CIGb zgqkts$y94C2ukQi{3jfj-tS!8KG1bIJ*Z{3Hd(SVvN>(}_C7VU9YeJtU%#Gc90F>K zV3xH7Pog*vITIDIx7@~%Fy{SLWQkN~6do?SL_N4c_waDphd0@5yt>xbA)$|OUc;EV zC9z>^Y8L&)O-!+YX&&vP_`y?RTjhEC;e6!O${V<0++Xr-NR@5T0#69}7$F{!0JGz597VRtd zvmJugKs4bwA?6X6k@vPq>(2@6hia4A2OO_?r#eN4K*RV#QKugg9L z3?jBOiFhMf+4fM`=8zvKU$)ua~4o-GoK zxqoZD&4ePhCzOWu#t9d~{5oP;TUU#LfmAaWOCN#AUSx3(xIFimpKBP0T)7nZxD@kn zp8VH$-4LtCA+#z}%ofO&UZ}V~z_LFit8{^_wF9bt(IGzUK9uXwq+ObNOmy1GbqB(<34$Dj7iXvmQS3!R)6eP@-ZhE>-9b029)~ag?(KOA{qZ&sA#yD{D;M7batyE|Itjvu z;c=^5dB!Aqhl_X?tjoEu46~G^+v)=@ni?5im~$YuXy!Dj5ZL;TR6#udHlsbFVZWO!%Atfm zW|FtHDt1v93df!dpN;^`NgV>>Qq#Thw#flJNii91@RU1-mV76%JL?w`cSJ0O>#Nlrsck{dP6 zRAvZ8S@+XfL3zS{k+Jo?*Kw;qrM&yPj=Q4w3#HZ!3s0?5lGwR%COiSBe%kMv2~bhY zTMLoKXANNxUhiN+e>kpcLT@An!n@0--;Jr7C^RZSdZX=*+JIZz z_OhjqQ_{Y_U0(A9;j_g4BZfwbi~H8-jc*x!hRn_<+P+hBk1bgiCg_sldl!vO6|-VL zfIwcdIzXhPAj3*k)&3Wfx2Mt2r!#u|P!>gJ1Kvl)BUB&xVa~qLt|FQ8pdxa%G76$l zOBI9*-&w$qkngBJO!@5zHO5Ox`;rH|_SsN=Uj#%oV5jM?%NiD!|Ld1PWg~ElaW_z~ zcFvm`wBnAvZb+-%D15Nt_UT52>=732>gh=c_N45c5XL$rbZaREQ?)?Kgu4a$ObeIh+K=f6ua^3pekikUO8FK zSXHdO{(P@^C1Tj*L+zCRn{bDbC-&`qPB)^Y3x;ml@Ot(a{xj9Fw!_=xuls;&Xr_%8 zn1M!oHuQEYQe2kH3ifgX?+Fz?~ zbLkrp7LR-EQqHa8Oni$c2A*?r6aAGu{UBY{doIahEJX5m!yA}qU2u=XZ`U5$CmRwD zy#mCl%7)n?qYkwzw==&?gb!2^LN{h;qNq)k9nG>{iX+k%SiD4jVzBy}W-p3=B)->* z5EOy+-!iekPtGYBP_b@`yFU7WbrvZv=KoH8VqC8-!Ry8iyd}NDiP2$hL9n+E>}55E zV_qsQ!>TP&7Vyo%^+MDDu37MdvDpt8P=Uq=u8ahJMsj<+F|m73u;;W=7%#NUDmET| zbsT=;n(#GnW;JJBl8z@7YbHr^NsX>pkQi3#UXQ&f^380DzAh?no#(!y9~k2?RzvYb z%!z>*=5AasuviBftIbSVZQaIfJ9OQBD(l4R^j-T}S#N9;krdtx@4S(|D*@!sMpHkO zce+DTMjnlS>FuOaQPSe=t_gY--`=)(b+&;iYufJMi7VN{enPy}DSYlaM^1A4?u@%m z%vz9IQ;Wj8cSHOXdBy*Rt@i%y?$Qly837XqPs6>)xU62V0KsKlfkQ2rI-$fY2HfjE z3tAF571{gCx&{PkmcG=A@ zz3iXpK|03^QPh^~Y#RGt#qbk*j5V5CKs!q64(&V;1O6o#7AQ61pFMTh0IrA~4sI{r zR`SC040R#9Ld(bL!?&$tr%RcSzB+9+W85kxt(HCjV*P~g+GP4_9Xaz#Z?b!16rt}R zhyCYhxq~r|IKk$BT>{*k)tsG%9p~mB9$(3v+hx2^k$d?Ae>-R#!R*m?=p8aD@zPRh z6lLdXq;Z!Hb>@a-bvFgd&90rJZX_bRLf!XtCEu+@QT)q-S3jm$Yy) zfPd}*NblD0MK-#nBO!IzAI#kS-$Xjl<`)GSKQv-Scr%>&!t19+Z%F5`t#(>kSv{RH zto&HrLDJpi712ep?*9L@;u$xdrl#%YtXpB5E>7i>3h*e{VdL$QoHO#L+)rg?E6@liU@u=;XI2zWeow{;6YXt~b>BZmocflrfQ7iv z#KxjUSFHQZg8*T3cR&E_4~o1t_I| zWj_iyT%Zt-iPxFG$LfZQD)=e1#xX|8>KIZl!uf`ILCw6*s{x0xN4*m@S11jNaVT^@ z;o1|h6YZg$mE2{<1IE(#xE5IR#rb$6)ca&Wv<_)&_NkoT1D}(R zVXjjuMApr9rbp#wwXEzFI(3$A4?mrR5~!FWyLS@>M#wI;U-GT%B#=LK}m!A(r z8xE!F1b{62?0dnT1;J5PzYYzS>ncr}&WK=@$p6Ij#hw30l-V??&N^FkH93OKEbl?t zlHs=A+nm+&mfQKCZ;aMKL4Y%Vb!nosnRSoGYv(tG<4e*H1&O-lZ239E=AFJXx_{VL z5FxY}K7Qa1T`_HXbSrXlc%=~wuFA|qh&KnO2kjPoR)x8NzPBHvr7uQ~bnqXBU(Osi zvsT_z^^xk0)AhCeNdT(bDT4mO1h336vz?77$6$cI=Qg}tC5X3%9TVnTi;c4SA8#S( z?}L4hU;JI(IQ}T&-*DYgvrCT=1iLIKvDT{Gzo_as-a5IVb+@w?s&tlg~tcAFUPKneHYUj zWJgw0uML>(BOqel-EEHh0=Y`P#<6uYFkz52{^aYWs<#Q(gX=Ly6{?)e5`!d-xMkoLQ3 zz~O7=L1|{Cd!H{wXR)_`#h1fv0{_PriFDyy)OK5QAMeJcsNRHw_4(#-@4)T!TYpw{ zmZY+dl{VfzzIUnLCCDXqTk&^&C(U!n%m4mWM2I23&@5f`vLW=JWmPZj(~DRfbUpG` zpT!yYx|*=)2nT`*Ut{EeN3w10=J1mFS%&Tav6+-D{uaQpY~gleUhaM%HYJqZuh!Fk zF<5)jSofw&@7CmBt)+LQ={Sxa?@>L~@a+m`ZvKEtkIOvc z50`*D;a3YEonxq6)ZvUgibByc5LsaBnj|}kEh%Fvi0F8@7pN@7b)*b=AJyu*jk8B;^^!M5k% z>h+&0*Uq0YA9?oTvHd2x=(*b&oDT0yaH$m zjrzV~(3WmL_ILJC?sU@g$L3?>YP_wC82<^AHtUpW(0@mr4^cnBIN9;+sp+}yiqu>$ zTWj0YYzk4JlJ66A)N4-3c<3Zp=j|rnst>6J)NnklEHayxCfb388k3LRrj1Y#jr4dgqg#q@m(=nJKH0rzzM$;w#mn>3Xfcn#!pAK7~mCrCwzZFiOJ4kK@zZl zsQ!6<{G7%2^)q>g)O{0fH`vz9C2?+nr@Ets5$^$Pjf>7T%emMa4D|Z{q%u=v$4<%@ z&pzc{!Kk*Qg1UKVkXE0D@mQ8hFLlU>Tq0zgv>terPYQsq0K4b3*!f{<+t|h($Do}> z!}j3`nN;Q#E3$LJ&;zvvcVorgp#VIbmjju14-JE7^j=G`ibs$eQrB^~p$le&`xd&*rFV)dsDczzpja2kRR93*V2M2xMsy6bY>h2wK9={uYwU6|l ztui?cI`bs%gb79DJYjTrqaW1^21cR+QvHEvQ`(NA$!CM$?)IN-Xz_jRSp&QKIyQCZ zx&P=~Qt|$)WARYo>xt&+4gC+)4l?PtZ{dw|aa+&a2D61DB6(knC8FH)Wx^jX$$vDq zg^R>#r<0d$Q_mwpy`>qJE_7i2Z zvvF4)8Y#^KxV-nbcjLsm$Yp2YS`D54x%C-?TYC585QY8@*y%0vD5;m^>xRbuSls)V zX3rR{I<*FAEsUil$uV39kFZ&pwKj*?(1GwG6o%=r22uU~uLMm>w8@%xFRB{FLM z2Z|6E(**a5=@#;sJ29&BKpO|^*Dwd_*fLqNRu=Xh`L6J3U+lB8atx(t)%F;@(e&fH z-*rXr9~S;ZKpgM?SQ7hsT2P)?+RQnabqXJ)4`p+`-6o@&l-MSI&aEr!D8gj%0FYhM?9s^%n+&showJo> zX5n2Dl`WvAAmG+l%Dgtp8u?-74=6U((6{!AB3w~-(09E z^ovyb6M!`)UBw$8@k?8aAL0Hs(xl^zr}viTz;k(#j+V>-_lgY<|3?Vo{o=-Ub}U&o zf;x~xWl1aCv_-P-S0>W^waM2|=nr#eOgirlk42SmkIA0WrH%b8orOWs(mmr*W{B{c7I5 ziTNp-GgkJ5w3_eh`uS0G!uj}JybD`3f~HiEh)=3||5;cKKELQb2cSTeidp(#SQxmp zpZP$b_Dl2ne}7eW8WQT?xxKBO@jkNZlzxrH(9x2&4y|M5UYInkndq+*86mLv^DG ztA+2T9BOADYz707I!+;a{n8wUKU%%17#dn#p-YS0aR;w#%`>Bcci*S5^Dz;#mHl`M zbH8_QpNn8jiTAjsd1_nke(&@T2b01`%JLhY5wRV+{N0TcSO@unJ%PKv5jIpcT4pEp z;aXozpogu3Zb9y1RrnGW&&C-{b$J5*IFhIA--9CKa|vmy_Jc!R_k^naNRJ0}Ud1x~&H86y~4?tFZCDnNf`V<91x2B zChfIn7HgvZ9Cl~Bu)U$N81x5Pg;dJ2p1=fK%nJHz*Rd?M0Hpq{o1epyRfk)Em*Y#P z98-&+F`+AO*1dTXvA7fWdz{3Y{Aty=riTAi1Qsa{Tl^&_TW70(%2_U0*xHE2kGlPR zapn`h|8hF^Dr=nwFzX|vsdP>iW>K4bKw7d+ttkE)bPmxOHmIOcfg)DfPuAnq{wVRG z!6X%`2$=L;2jtVxuGMK^Kj)GrdGnN~>hSBh#+<=4=)$??JvG$BH1fI<{L1|ik}nyj z77d3@_gaL&an~v*X45I)+xBlGl9Ohl;KDYG{5qWT4c&FLnJmkt>Pv<}Og=sok*?(!8o{p&iwrmW#+kXGNDat1twpS^< zo%;fr^@$!+@#oQ#UdPN$Pa9>P!m#R4ZIJ%e7qRk%qi&lz#2LFP?3@d>w}GtZxuBMZ z#_yIQotFd#*|^JZ-+ygC#q94NQC`%)jmJti$?Pvi#TI>z2>o` zvh>TA(0W;VL5VuJaeHS4PTZd#r?P#jNP)&1*+h6&`Gy5xVy#cu`#&Y6r)U<+Y?z`~{7$DMj zm|Rl5Kf2f^a~v}td~Zy6f9fh2boB1RFM6q`3^xW!CWeD;BwbSr=N2U!%_uW-K$o!j z=>G|A?bdXopfFbpyBG(`8(;e-S6V=Rga9uLbf1B`5!BeMxC*X-8_NqZk8!p;_M0Fy zjUe5la0ePx3}lNadR zFs3*KY4}ip)`*$W$HIkgIR?jr9V{YgO6;Q83XRP-LkLxh1dlP+kt^k3$-B$g@@5mH z*g0SqBWA|z|6hgB+7cab=&8f;!&vPd4*VomF@1|Y7VxT4ZjN~wZoQsVSw8tw_%F-r5yA zmS-R@(H&5b-W#tzikVqwXs>E{At`QB#(G(`c=A(iO~eGNs34jA<2LJ*1KMY0NHn>+ z?bhfxP@2Dq_db;!DJDpR(W9yE^WOlGk`i z@Ri_puT%$ju8-Jat!fbwiS8gaA8oFM0rf3WSJB^;7oPNwM-%9_SXK(sS72|O( z%BPU@<$MnaS)y)XE=i|>ELGp4JpM!Dm6o|Dgn_5Edo116a0~bzRqPhss!bLpYkwH) z41oh#U`%D%gaKf=6XYT@ITzp<+*OXrLID9P2SZ|P$fa6jMr|03+o2bnQFM^eid;{cbl0C$AxBC;D$%dF8V&StqF7WgyyN?64e@q zO)>DX%@REQSVkmzLqlxuZM(gVt!?Cd!8b!%HHUXt)mM&Vj@r0WGu(u<*r^sZ*E3^o z^Am0}C&rRC@POF`C5zj1|7)IQa&At(4JR`9I={!xK<6Dsn$fcoCRB;(?x%UmEs+Gv zl;_W`*FgsfM!bDS#`c(jF=tM#WkyH{>DidM*YB?ju8&ZTqpEwWa#jlya=(Kl6=LVR zB|zs~RbU9KTO%?@1OYjQC2+|+fqhnv9|DV2UCApofd8PDt{aC_uMTRb*){mZB}vpn zoW{CG!71f8_9?TAK9s{u7WE$%?`G$iKo3?DS6d^ROfo^a$o1F+?~?DU2KV2RZsd-= z*BYpoOaTT{O8|mLX$C`eu^nEw6ATm+`tg#nI}~g{u{e-_k$i4C$Fn5_G_2qwx-EGD zgWyEY`%A%K)D5VU#j!oFn`a4GYgXZ=B{*+D&`{4%%+(s4@uS5?5D~v|0(}DM#@%$!`*m5fezSv+Db%4Xs=PPB zqPaI2z~8Ia38CNd=@o*f`2YTJg9S*6PTl?pW=SA0n~HiYrd#W?*3MqeJ_8{tX-l3M z#!Pl3iJ@B-@?3kvUrd=cwfsQOD_wYO({Yxy9m>4bUkyn5?WFbltkmc6t%u8ON24^Bft}1vRDo-r z3N4l8OIqE+&TJQ9K@;WEy3<7!sj~Iy+4ZT$>}@D^2l;OWsVdEV2Wj8EDb>W>=cr7U zx;5=C(%wSm$0hKKr22VWmp9FDb`lEpHSw7<`V}QtOR^2CWXOsd8}scWxt)*Yf4)b( zwoB~#%tNZ&0Yh~Cl>l?lfP#nTX~)kkMh4R9QzQz*!=Ih?itsdcz+u8JJb0K(r9mk* zYSnADgpb=eDL7CDFpJZ(&5Mgm5LIlG`hMKTqgeqo)gWH6L?}JM@BCnq+y36d^5B{% zHN+IyP3svi?una#CxZRzvbYUCK~bj<4s}yv;OU z3d6~vMK9p(T8d3nG4xP^v%!6i3m={K&+V=64obROUwHxZrWPpZV~2?W?`*fudb-oR zT2Ch<>rscrQI;LBjCi%?&ibjBHFDpKIwm9}8R2F6lUqF1O|IPax4u?U?c}HJr;Fbj zlkKVHN}QtLH#NrD5?jc{Z@KA)HoNWwemyL(;_iaSx}k|7^`YaU!W;73Y&%G%xaB<@ zp5GLpOPOC%M%^@aGis%6o?oDtjL@f$JM@R*ex1`q0Wv{!d$`ftxn`+s)0;nb0caG9 zT3~7{xhO#tovWVjEK(SJ6~CT|_-&s+AELW$yfKRUV((lb&7*`5KYFEZ=$GIAy%{l| z?|%93&5DTdO!=}Xq}An=Sh7;bI9$mvGqyFNt+D6yM5{~TG2>F2Kh*%9AmXG25$TEM z>Pp)g;Obhhr5;ZcT>|Jca$pk8y2^EBde5^-Ki=B8XBe$L8ed0Gxxf8pPmuOYG0N3N zOL+U^t4fMH3oJ^^mHOiSXTqKSAI}fm=?oo%3d@t+Kz=SdgBdl=7CfGoV>GFS1E<)~=^~uhq&o z60e8*TW*%lFL?dL^(dxVOnz|MtQYZ5&rdeFUt1R~(xt3*vL^(4C8}p z7W{dH6M7+{3Cv<|Z@*GPGYGlV^AWRpKbK%N@7wQF|C7)ix4YMblsLUurK z$nn0vqS&78Zi~-z^*koXPO09>3li7;QAzpvw&mpKUg_%BDQ=e}WS#CL+tQ2i5=sxh z=b6NHZ1xw5ucXH^=FNM(=>AxDYyLs$t^HGxYzSOSSYHd@S{USFp7d|kM(4g9MTLB1 zIq5rJ40QPf8=C4M?9|iF0cYwoSi1sdCQm=4Td_~W3#HBoC3MINn$jB?g0;wOTr{5C zBtAc{3@T-i<;(JJWW84X=1;Cf3~Xk8oIU?x{pL572ZWs@u0KhaTRu^139%EBvmpRQ zbJi0!wEe6|V8ly$l5%vh0G7F8*w=CQ4dDNl4%ht0-{!`9fNtJ*ygp|CeU8hi+ zuKWo(6gkTT?3Vq}Ex}S=&O%LMO}qUU8#;=+PVWxvbX6*uzVa~c462N)sCVkF`-}j1 z+>w>G_Z6obKjdGNJWHZ&u#f&3t>T#|DOcA$_G+H|P%ZMCKxJRdV!tUS>wbh0?$mu} zPAB{+Tg_X?Dx0y$Ui4v_YLsO4)K0J1v8R3yhRd|Y9K*bemB@k~4%;b8#p_8Fq{&YQczF5){5dAGXVI;OqPy2enslUaiWVAhNg zBvwSX8VX&|lW6)NwV!s)>PbO`4~WM2U|C2;ub3qvX|jo-i>SX^4&j{9Lbn131}*p9 zKGYL^uz8MTXG_O!j>*-Jf{3GMdPnVH~3}DSN z@8+-fA-Qa70!cteGB?Ho7cum=zKRiKDefr{b1mjSsrj^*!E&kT;zVg79u?unT#|KD zS#3fmZyC_^ID|ekuVnMOL0J-QrO*w=arbTG86m6P-q)^Rz%+LZwGOc*;|E>JFU{Lj zO{^xCW$_r%vG}r_6nNJ6+;?kmJvQBdbCFSMfJSRzjfGR%R_H&viQq(YK3zHI=U@@( zei-0B+#VpQb)2$odE!qE>*RF?{{OTFPsFf7_4F%b3QlN+83MB)oqI)TBdAVz;Ma-# zsoHIDv5=wQs+s9}P;HwE=vh7J4!>oA4_OdQaH8_~;PctBmj2v!Nmw80*4#Al=-Z{{oe0I3weKb<$a=f$*{5s7; zzZBwNi)?3E+WGB(J+wxk*VJ#i%8`=Pcq*Twdf^Z|{dCzg(QLrr$f~bO==?ov+MQ;Y zQqqZ1Z1?CaMJv<-Gn{T_i)2d(5>1NX!ejC1yxQg{&TFe&-TWuyK-Tq zwMRFHF8a4OAfSnmQlnmjIAC0~i&!^zD(lYr?%G8f`zq zftf`4#`&>aG2Ii~vIswkp_U{GUzI|`%elm)FQNP#>^$0%FRB@*k-xaBEd~db*y#nk zt1IWcE9(We&gvb#;GrMD6$Q4Q0FQhAwqIuP8;F=HTeCS#+OXURWg}RrWU@H8UUbc{4goKO zbJZJo_S{|OvN5gD0Byy*1MXniDnT!p>wa=boEg?V!8ZD@;dR9A@<$1X1ceFqiG24cS3ENzVM@i zi+FE+-Wiq*19oovH4ndIvDmsYS`hXsQ9Gm(qT|PyhdMa@$R&lp6nRs>)NU6N8aC;` zX?~BJX{|`aTcHs{a=^Q^m8AAMek0Qj9{-h9Mer5A$7XKvh-wsL*S(vOKgxfsr6?q$ z1{W1Q+Ca$enUPGi?N>pwYda9+OqsbR`WnQcPhhFT?0#55hF+=x6g0Y3e7e&yPqBba z?Os}}&tBh=MT4U>(Ewhg9dx<@_>+ht&$rD7)zH1(tjmFMYxCXKY2#whT)3OkGtvZy zjm#vszqdy-C}WYJCQMq>h*#C>-niFX*b<0Jlh`YO2(u5JhC_Xo*nuGESc3a}bmMH_ z!Ftnw_RywqD&cyOcEJcRY26Zza6hvw;So|p=!ZwxKgX!tXs)>k)IuCz>wJ29e<#%M z{%K;?f6#pFcswdcF)bER=K)J==HCkdK>z*Z|G4<9hi)7SXEOljSptO+ga7C>y z_CKimKfmj`L^ErtlKGGc>P^VHP#p%3VPP?<*+~bM-UR*-wrY91j(OziFP}DrA`&BO z(qbu9L(|kyf<140TjJL+WdQ$03n(XY4MO<>{eJ zj$8rH@Bcb0bi56(`dG38)G->Ol#4#DmsXT!R7!nyex&rv<|}fBXq48+t|iyAs((f{ zXaCgR?4<2-o;YV6U z&i+eW1Hi%0CA#>ypPQaCZh@^%%OJ51Dhmd^LGh7>7Jiw2A4>j#`tb>b!Lq!~42w*T zNA|PpWu~J7X}^kc3@QE4zv%BCVKs4HqaST|FKWqI4Yx~YEP0gLsgJ$VHEQJjL_-vU zm*rtkehJgPotju!=Ba1ha(|x|5WF_Kbv@@KE*uU68XMdi+e5H+{{iR!#pXIScSgTP zO&*JeCF_WCC?;b{H#H@BE|VGRtIy>xyIjps0r)te4uUO_57VZ7OdAHrl0zh8I9WQK z*36!U?%bL!Jl<{h&8%Uc9`*R`RWZ4m*4L<{zvu!)P5aysS6J(>V~z4oCh5su1NR(! z*lr!|I?cTu5RnKZn1ixW?I%T(-z$%C^OcE7Z}~O3MLEZ7e4hFw1S1|^jDHoy@2H0> zt8KV6l@Au|y6fvol8UYqeH4WUB{S|Um`Cbd@7|Z;u?~DrSfI;Wgh0 z0A+Hcb5|sxe!?9p-ec{VAKEJ^ThG98)K;ry8Yw3#MLhFXzUv3GL;LDE@E6}p@N8#s z&83-D^gZ!zFZD}iPfihRF_Z9Xsyol{#0tTxue&VT0wqhL!`ra~jWm5KfA^yXeLjzv zhoa?aW7D2pOFX-NrG46qbIWLoU~3RSqF-FINW1LU6xzS>jSPja681dSQ^nl-IWJ8g(<6K>PDHT5grr@|7*NM=)E6IN?A9-AZw5 zF#?)a&Vl#HR2*B)XwR**^k0jA2l2G+zQx~I$vLnRa>K5Aqx#TpuYW3>v_6*a*yt-b zs^RIR!n-)=RL2?#65!(<%fF;UCZd!>(l;AeKbR|BH-8Aa+#Z}^ayW(+_sy|riX@>e z+Cw_%|7(K=uwS7;0DC^#v&=7k1kPFd&b*s~M@x?i3hHjZNq7ggdvFqJf2*oR&2>aY zKruKg1(X`uQXNNFoI2%PxGNWhqt8_I-QvhqJ1DwPHh?gRGz&K+`v!BqZx9ONlAEIMh!;oWd)iRnXxVhS1N@-!Vu zj4AP>s1ItZ&<3<}1!=F6md`=-X?N23lJoLs;6BfvrRvyv!OZR;Y7QH0@}@@(SseMj zfmy5M8X7qJZzQDx(74ucWljUEhe!xR^4bb>M&rKF*;ws~x$Kwiqqu};w2F6r27Y^Z zOq4?;rZHA8=@vmswciCPy!z{#QpV@4m2I_S^QsP_bp3D1ELl1{Dk9Q%)x4y~uDmLV z$FabAx{|549ZZ|Jex0l77;aN};rbfrInwnf{AhzQDw{bf40uuZkF0dr8)Qv#K}@Jo zfI<1MbZH+tHorr7DzRS6cs9wenp7yvxdxO zrNpuKjk#-PfG=w%f6~~CB0Ag2*F35EY1`D>x_?ghI;08Uz!%!}ERln!g=CIRyiDDP z+85S7$)|SqxKz#GAg|IzAw4mrkGtz!*$#ujq~LMX6{EH1KgDt}sR)mJ&0k>n6ths+ zXlRD89Ao`GdFU;B{KP2**dUvGU&q+h7PbIg_)DOqJBt$nCKQ@$@fX_!YlTz z$NcI77&FW9{t=CrPdwL2mWRf)J60!8$iQt}$s2DyO3C>ioR%&Zpy_8(Cu zNESPjU_mRHX8nc{p;$S>iWY)4Oi`L@rYITomp7+_M$7ilH~UUK0+W>vq>DC3S{Q}= z95C@H@&a5kq!<4xjPU(2%C6&*vq*y!Pu}U=PUf<;@#OvnV&qz92ZbBEv2;lr41!7> zE6Xx(2`aum-AwtRcXaC6jtii~6k&9ZfB$nYkZ-STp*&uc%P@0w(~1^|qs7H6=rBY~ z3~Bc#(()ZOwmg?^(T{QwHbzqi&iDRfN@R>8+KM|#lcEWF>*q2)BWq>KHuzeUsurjp zB$<_#lE1~&rtFEC-fM}r++##+OFUdBrk!i7Bna9Z>TyqV240*79gUTK43?7B5!J`F z3ih)RdzAfaK;PC^Niah&!mG@YsoQm4!l|#;>_Nnk#hWc!caTBGixwX7r8e1yf^%Cb zY`QYe22|DCXJpF~!YsEMxNp&pAJ2`~n>BHnt8;4`<#|z@y?~|kNIV2>f1oJO@6ug& z8PJ2iL&lhGtqR0e3alxtu(?SB!G@KgC8MRO+1-34}yr||NN1p6>yEcFv0RLXj zJc4RxFCG+VaC~j#P6N{*%O@J5Gp&cET>_uE;MlGwq4wW#>b5XEk(09Fqhyix;OEqQ zaj-yq?avBf8!F@pt@cZKqxPc&{&_cdk<@wM=5kL=Wd+8ucRha9H;WGc?M>{hF)E+= z4Ot#O>WHx#ol01nl{hdWbN*{YCTGgiq=NUgW>1J&)_>7hpimy{y!yWhDnRjZDGbT5D(go!Pd*NiX$Gq*#Vz5ky zjwL8El?4+v0fYm98(fMN-@`k&lW*lT&guX2XVhV*oF=|k-Zaa<{E$7lPIhsq)Un5D zKZ|CtXU&e2jX^TASV88d>z2{~W{GtLpAD>o{V^r_o@2a)^$u!Y_vQ;3xuDGPT?ZI> zsffuZcJ^m#Kk;!=w@Sli=g)wy?VI zu+-I_lm1GW)susYK7KL=!x6~UW`n739k@{$hpeHhsIP-G0Q@6nkqGpPK3*P`4_e}fzdN!3OuFTj6 zgG#@Jh}LbTJWs$U*}e9!0)F>_c`xa0EZ^>rw_8u^y{b~`DUyBu*GW{SdW1(4sOe4G z$vowIWKqFvpcC|W6);rUKxQtco%|smu}DrB(PV0D%|6R)S7RR7oAOXKIRDk}c2ejL zWbq@1tVz!FVf8-7yX$z4G$05w_D;ks<*fMhd8%w-#NBD`g*KfI+w3f0^3-i7)#f*e zo;Hpu75X9KBrV1;bE`atX*F6k`E*uOg#a_7RzZ#xm%NP;0aARuEDjPeD<|LRxEJq( zUyL*3vr%&N{CVSpofR9>FI~=Goq2jYXh4k{8!n0B^5}HEI_=z_A}>M{MJ2u8@k)4d=BwEEUx9Glr?b&v$noQ^@dZR zZ*K7{+i%+TOOTbcMU#&94#HpX#9u+^i#6YPf<^tv4-a1QK)SHpH%%Ig_hv=xeGlS< zgS*^q%Zpo{PTIa64#cF4L3gEiPtMcMVAaqi@sS?$fuo@A378la`&rR#x5S_pY}Zs# ziX~hXf)22v`oUO|c|nk*ep9tDDv|qC82%l71s4PcPmznnS21ljTx@>tQriui`eFNm z`$?EZPUF&b+o>m?Yz`Xhk{yAZ5Z99M%382>&Xr4?#95SLQ%ohP+EIZx7p(fXm3b|Cw#*K|Q?+GQE$BGM^6gInCCZF44GijK) zOmok$wXsLd4vPB42IUyTB?Oz6*bp^a-+@>-$v0vsG)9zcB1Zk`UBiXd3v1Tvd^c;6 zkJ<{A?}Su~V#@$sTYR1MaCWS>G&`mWr#G|u4UA+h6!Z^S8q_uYTCJ%!w`4fa?Y~dFtNZ;^l;?O<~nF0u)h(FI9 zSCe{eU(Baux_@#Ye@rU0+I_h_KEZe6%1Nwmb+Y@*G|!*ezbL}C5X+iCvPTu8lx_nP zTk2!ar0I11IF4Io(@^68`Hue8&S$OY=?vfI8CgH2hU5hPeod-b<*-Uk>6XyuLCfDy zq;hFgN472d&>3f!wLU;CiRGvJXrR@O6pQEp-_Xw537#P7*W9d+Ak*>L#d)&T?tQpg ztEsm>FdxY10!UM?6{U^~dV(!ymQ(@JS4mNJ zy^q5W<`YIO!&oWv@C zqtKgZcJzPWmdDyWP5v5U$9&o8A+BM*@Wf&ar|+OC*3Uwir+JvIY5oV)=Nw|;2@j(+ zBG<-iiQrQnOU!ui$xHX9)Q5M*+`kes?nHbBp`#r+04j?Aka%env|$N-duW@e0f;q+ z5BE3$C=yesVDSU@3Of=pbzr9^%pJ-&3Z8A6K2*@R4xx{i#@h^ z!;648c)(-?&p+R^94#&r=IO6it-r~+8TTJP_+Nm3K2h6^MR=0;?zu-ivE!saNF$&B z7qIVs0XoxqR!?=k&AZZY1+Yoj%Oql-p;_Rf8O>F%YJWs5P