From caa9d00ac4d54047a3e4dc06555cfba443c84605 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 10:39:37 +0000 Subject: [PATCH 001/130] chore(deps): update dependency prettier to v2.8.2 Signed-off-by: Renovate Bot --- microsite-next/yarn.lock | 6 +++--- microsite/yarn.lock | 6 +++--- yarn.lock | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/microsite-next/yarn.lock b/microsite-next/yarn.lock index 3a632b3079..54b353b415 100644 --- a/microsite-next/yarn.lock +++ b/microsite-next/yarn.lock @@ -8361,11 +8361,11 @@ __metadata: linkType: hard "prettier@npm:^2.6.2": - version: 2.8.1 - resolution: "prettier@npm:2.8.1" + version: 2.8.2 + resolution: "prettier@npm:2.8.2" bin: prettier: bin-prettier.js - checksum: 4f21a0f1269f76fb36f54e9a8a1ea4c11e27478958bf860661fb4b6d7ac69aac1581f8724fa98ea3585e56d42a2ea317a17ff6e3324f40cb11ff9e20b73785cc + checksum: 740c56c2128d587d656ea1dde9bc9c3503dfc94db4f3ac387259215eeb2e216680bdad9d18a0c9feecc6b42cfa188d6fa777df4c36c1d00cedd4199074fbfbd2 languageName: node linkType: hard diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 3bc81619e3..34ed76e9e4 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -7369,11 +7369,11 @@ __metadata: linkType: hard "prettier@npm:^2.6.2": - version: 2.8.1 - resolution: "prettier@npm:2.8.1" + version: 2.8.2 + resolution: "prettier@npm:2.8.2" bin: prettier: bin-prettier.js - checksum: 4f21a0f1269f76fb36f54e9a8a1ea4c11e27478958bf860661fb4b6d7ac69aac1581f8724fa98ea3585e56d42a2ea317a17ff6e3324f40cb11ff9e20b73785cc + checksum: 740c56c2128d587d656ea1dde9bc9c3503dfc94db4f3ac387259215eeb2e216680bdad9d18a0c9feecc6b42cfa188d6fa777df4c36c1d00cedd4199074fbfbd2 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 56fa82473c..fc273856e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32407,11 +32407,11 @@ __metadata: linkType: hard "prettier@npm:^2.2.1, prettier@npm:^2.7.1": - version: 2.8.1 - resolution: "prettier@npm:2.8.1" + version: 2.8.2 + resolution: "prettier@npm:2.8.2" bin: prettier: bin-prettier.js - checksum: 4f21a0f1269f76fb36f54e9a8a1ea4c11e27478958bf860661fb4b6d7ac69aac1581f8724fa98ea3585e56d42a2ea317a17ff6e3324f40cb11ff9e20b73785cc + checksum: 740c56c2128d587d656ea1dde9bc9c3503dfc94db4f3ac387259215eeb2e216680bdad9d18a0c9feecc6b42cfa188d6fa777df4c36c1d00cedd4199074fbfbd2 languageName: node linkType: hard From 5c1abc3bbfda6be79fbfb002c93c96b71154e771 Mon Sep 17 00:00:00 2001 From: Mark Anderson-Trocme Date: Mon, 16 Jan 2023 14:34:09 -0500 Subject: [PATCH 002/130] Feature: Make workflow a link in CircleCI plugin Signed-off-by: Mark Anderson-Trocme --- .../src/components/BuildsPage/lib/CITable/CITable.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx index adc8a569bb..8f99f1f69c 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -212,6 +212,10 @@ const generatedColumns: TableColumn[] = [ { title: 'Workflow', field: 'workflow.name', + highlight: true, + render: (row: Partial) => ( + {row?.workflow?.name} + ), }, { title: 'Actions', From 1616e06047ebd78e2904e93cfaf9264a6479a3c2 Mon Sep 17 00:00:00 2001 From: Mark Anderson-Trocme Date: Mon, 16 Jan 2023 14:52:47 -0500 Subject: [PATCH 003/130] add changeset Signed-off-by: Mark Anderson-Trocme --- .changeset/slow-moles-sin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slow-moles-sin.md diff --git a/.changeset/slow-moles-sin.md b/.changeset/slow-moles-sin.md new file mode 100644 index 0000000000..13527b509e --- /dev/null +++ b/.changeset/slow-moles-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-circleci': minor +--- + +Making workflow a link From 58d9145eef563e72685bbc5fa04fd5c64de5c6f6 Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Sun, 22 Jan 2023 21:05:12 +0000 Subject: [PATCH 004/130] Added page size support to MSGraph client Signed-off-by: Alex Crome --- .../src/microsoftGraph/client.test.ts | 5 +++-- .../src/microsoftGraph/client.ts | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts index 05693149d8..783c16fe6f 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts @@ -70,7 +70,7 @@ describe('MicrosoftGraphClient', () => { expect(await response.json()).toEqual({ value: 'example' }); }); - it('should perform api request with filter, select and expand', async () => { + it('should perform api request with filter, select, expand and top', async () => { worker.use( rest.get('https://example.com/users', (req, res, ctx) => res(ctx.status(200), ctx.json({ queryString: req.url.search })), @@ -81,12 +81,13 @@ describe('MicrosoftGraphClient', () => { filter: 'test eq true', expand: 'children', select: ['id', 'children'], + top: 471, }); expect(response.status).toBe(200); expect(await response.json()).toEqual({ queryString: - '?$filter=test%20eq%20true&$select=id,children&$expand=children', + '?$filter=test%20eq%20true&$select=id,children&$expand=children&$top=471', }); }); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index 26da0cb64b..a1fc77b4fd 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -52,6 +52,10 @@ export type ODataQuery = { * Retrieves the total count of matching resources. */ count?: boolean; + /** + * Maximum number of records to receive in one batch. + */ + top?: number; }; /** @@ -188,6 +192,7 @@ export class MicrosoftGraphClient { $select: query?.select?.join(','), $expand: query?.expand, $count: query?.count, + $top: query?.top, }, { addQueryPrefix: true, From c5b119ad9cbb1d35e5357a6319237320cda0637d Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Sun, 22 Jan 2023 21:56:39 +0000 Subject: [PATCH 005/130] Updated MS Graph plugin to use larger page sizes Signed-off-by: Alex Crome --- .changeset/young-points-drop.md | 5 +++ .../src/microsoftGraph/client.ts | 12 ++++- .../src/microsoftGraph/read.test.ts | 45 ++++++++++++++++--- .../src/microsoftGraph/read.ts | 13 +++++- 4 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 .changeset/young-points-drop.md diff --git a/.changeset/young-points-drop.md b/.changeset/young-points-drop.md new file mode 100644 index 0000000000..4309d54c2c --- /dev/null +++ b/.changeset/young-points-drop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Increased default page size to 999 (from 100) to reduce the number of calls made to the Microsoft Graph API. diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index a1fc77b4fd..fd640e70ba 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -348,8 +348,16 @@ export class MicrosoftGraphClient { * @param groupId - The unique identifier for the `Group` resource * */ - async *getGroupMembers(groupId: string): AsyncIterable { - yield* this.requestCollection(`groups/${groupId}/members`); + async *getGroupMembers( + groupId: string, + query?: ODataQuery, + queryMode?: 'basic' | 'advanced', + ): AsyncIterable { + yield* this.requestCollection( + `groups/${groupId}/members`, + query, + queryMode, + ); } /** diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts index bafb2ecaad..0f9ddb0c13 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts @@ -140,6 +140,7 @@ describe('read microsoft graph', () => { expect(client.getUsers).toHaveBeenCalledWith( { filter: 'accountEnabled eq true', + top: 999, }, undefined, ); @@ -186,6 +187,7 @@ describe('read microsoft graph', () => { expect(client.getUsers).toHaveBeenCalledWith( { filter: 'accountEnabled eq true', + top: 999, }, 'advanced', ); @@ -228,6 +230,7 @@ describe('read microsoft graph', () => { { expand: 'manager', filter: 'accountEnabled eq true', + top: 999, }, undefined, ); @@ -278,11 +281,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq true', + top: 999, }, undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); expect(client.getUserProfile).toHaveBeenCalledTimes(1); expect(client.getUserProfile).toHaveBeenCalledWith('userid', { @@ -338,11 +344,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq true', + top: 999, }, 'advanced', ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); expect(client.getUserProfile).toHaveBeenCalledTimes(1); expect(client.getUserProfile).toHaveBeenCalledWith('userid', { @@ -390,11 +399,14 @@ describe('read microsoft graph', () => { { expand: 'member', filter: 'securityEnabled eq true', + top: 999, }, undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); expect(client.getUserProfile).toHaveBeenCalledTimes(1); expect(client.getUserProfile).toHaveBeenCalledWith('userid', { @@ -527,11 +539,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq false', + top: 999, }, undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); // TODO: Loading groups photos doesn't work right now as Microsoft Graph // doesn't allows this yet // expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1); @@ -603,11 +618,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq false', + top: 999, }, 'advanced', ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); // TODO: Loading groups photos doesn't work right now as Microsoft Graph // doesn't allows this yet // expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1); @@ -680,11 +698,14 @@ describe('read microsoft graph', () => { { expand: 'member', filter: 'securityEnabled eq false', + top: 999, }, undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); // TODO: Loading groups photos doesn't work right now as Microsoft Graph // doesn't allows this yet // expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1); @@ -750,11 +771,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq true', + top: 999, }, undefined, ); expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid'); + expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { + top: 999, + }); }); }); @@ -887,6 +911,7 @@ describe('read microsoft graph', () => { expect(client.getUsers).toHaveBeenCalledWith( { filter: undefined, + top: 999, }, undefined, ); @@ -894,6 +919,7 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq false', + top: 999, }, undefined, ); @@ -925,6 +951,7 @@ describe('read microsoft graph', () => { { expand: 'manager', filter: 'accountEnabled eq true', + top: 999, }, undefined, ); @@ -932,6 +959,7 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq false', + top: 999, }, undefined, ); @@ -960,6 +988,7 @@ describe('read microsoft graph', () => { expect(client.getUsers).toHaveBeenCalledWith( { select: ['mail'], + top: 999, }, undefined, ); @@ -994,12 +1023,14 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'name eq backstage-group', + top: 999, }, undefined, ); expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq false', + top: 999, }, undefined, ); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 0ed411bfea..2541416777 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -37,6 +37,8 @@ import { UserTransformer, } from './types'; +const PAGE_SIZE = 999; + /** * The default implementation of the transformation from a graph user entry to * a User entity. @@ -107,6 +109,7 @@ export async function readMicrosoftGraphUsers( filter: options.userFilter, expand: options.userExpand, select: options.userSelect, + top: PAGE_SIZE, }, options.queryMode, )) { @@ -172,13 +175,16 @@ export async function readMicrosoftGraphUsersInGroups( expand: options.groupExpand, search: options.userGroupMemberSearch, filter: options.userGroupMemberFilter, + top: PAGE_SIZE, }, options.queryMode, )) { // Process all groups in parallel, otherwise it can take quite some time userGroupMemberPromises.push( limiter(async () => { - for await (const member of client.getGroupMembers(group.id!)) { + for await (const member of client.getGroupMembers(group.id!, { + top: PAGE_SIZE, + })) { if (!member.id) { continue; } @@ -380,6 +386,7 @@ export async function readMicrosoftGraphGroups( search: options?.groupSearch, filter: options?.groupFilter, select: options?.groupSelect, + top: PAGE_SIZE, }, options?.queryMode, )) { @@ -401,7 +408,9 @@ export async function readMicrosoftGraphGroups( return; } - for await (const member of client.getGroupMembers(group.id!)) { + for await (const member of client.getGroupMembers(group.id!, { + top: PAGE_SIZE, + })) { if (!member.id) { continue; } From fb568e2683697a64aef7c9ffe1d140d6ed2eb314 Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Sun, 22 Jan 2023 23:23:26 +0000 Subject: [PATCH 006/130] Optimised loading of users via group membership Signed-off-by: Alex Crome --- .changeset/slow-carrots-sniff.md | 8 + .../api-report.md | 16 +- .../src/microsoftGraph/client.test.ts | 48 ++--- .../src/microsoftGraph/client.ts | 43 ++-- .../src/microsoftGraph/read.test.ts | 78 ++++---- .../src/microsoftGraph/read.ts | 183 +++++++++--------- 6 files changed, 185 insertions(+), 191 deletions(-) create mode 100644 .changeset/slow-carrots-sniff.md diff --git a/.changeset/slow-carrots-sniff.md b/.changeset/slow-carrots-sniff.md new file mode 100644 index 0000000000..d32c2a281e --- /dev/null +++ b/.changeset/slow-carrots-sniff.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': minor +--- + +Improve performance when loading users via group membership. +Users data is now loaded from a paged query, rather than having to make an extra call per user to load each user's profiles. + +Note, there are still additional per user calls made to load user avatars diff --git a/plugins/catalog-backend-module-msgraph/api-report.md b/plugins/catalog-backend-module-msgraph/api-report.md index a5abdc17c6..09770614b4 100644 --- a/plugins/catalog-backend-module-msgraph/api-report.md +++ b/plugins/catalog-backend-module-msgraph/api-report.md @@ -70,7 +70,11 @@ export const MICROSOFT_GRAPH_USER_ID_ANNOTATION = 'graph.microsoft.com/user-id'; export class MicrosoftGraphClient { constructor(baseUrl: string, tokenCredential: TokenCredential); static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient; - getGroupMembers(groupId: string): AsyncIterable; + getGroupMembers( + groupId: string, + query?: ODataQuery, + queryMode?: 'basic' | 'advanced', + ): AsyncIterable; // (undocumented) getGroupPhoto(groupId: string, sizeId?: string): Promise; getGroupPhotoWithSizeLimit( @@ -81,6 +85,11 @@ export class MicrosoftGraphClient { query?: ODataQuery, queryMode?: 'basic' | 'advanced', ): AsyncIterable; + getGroupUserMembers( + groupId: string, + query?: ODataQuery, + queryMode?: 'basic' | 'advanced', + ): AsyncIterable; getOrganization(tenantId: string): Promise; // (undocumented) getUserPhoto(userId: string, sizeId?: string): Promise; @@ -88,10 +97,6 @@ export class MicrosoftGraphClient { userId: string, maxSize: number, ): Promise; - getUserProfile( - userId: string, - query?: ODataQuery, - ): Promise; getUsers( query?: ODataQuery, queryMode?: 'basic' | 'advanced', @@ -231,6 +236,7 @@ export type ODataQuery = { expand?: string; select?: string[]; count?: boolean; + top?: number; }; // @public diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts index 783c16fe6f..f621b4d924 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.test.ts @@ -135,33 +135,6 @@ describe('MicrosoftGraphClient', () => { expect(values).toEqual(['first', 'second']); }); - it('should load user profile', async () => { - worker.use( - rest.get('https://example.com/users/user-id', (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - surname: 'Example', - }), - ), - ), - ); - - const userProfile = await client.getUserProfile('user-id'); - - expect(userProfile).toEqual({ surname: 'Example' }); - }); - - it('should throw exception if load user profile fails', async () => { - worker.use( - rest.get('https://example.com/users/user-id', (_, res, ctx) => - res(ctx.status(404)), - ), - ); - - await expect(() => client.getUserProfile('user-id')).rejects.toThrow(); - }); - it('should load user profile photo with max size of 120', async () => { worker.use( rest.get('https://example.com/users/user-id/photos', (_, res, ctx) => @@ -330,6 +303,27 @@ describe('MicrosoftGraphClient', () => { ]); }); + it('should load user group members', async () => { + worker.use( + rest.get( + 'https://example.com/groups/group-id/members/microsoft.graph.user', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + value: [{ id: '12345' }, { id: '67890' }], + }), + ), + ), + ); + + const values = await collectAsyncIterable( + client.getGroupUserMembers('group-id'), + ); + + expect(values).toEqual([{ id: '12345' }, { id: '67890' }]); + }); + it('should load organization', async () => { worker.use( rest.get('https://example.com/organization/tentant-id', (_, res, ctx) => diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index fd640e70ba..c644e545d6 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -234,28 +234,6 @@ export class MicrosoftGraphClient { }); } - /** - * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User} - * from Graph API - * - * @public - * @param userId - The unique identifier for the `User` resource - * @param query - OData Query {@link ODataQuery} - * - */ - async getUserProfile( - userId: string, - query?: ODataQuery, - ): Promise { - const response = await this.requestApi(`users/${userId}`, query); - - if (response.status !== 200) { - await this.handleError('user profile', response); - } - - return await response.json(); - } - /** * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto} * of `User` from Graph API with size limit @@ -360,6 +338,27 @@ export class MicrosoftGraphClient { ); } + /** + * Get a collection of + * {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User} + * belonging to a `Group` from Graph API and return as `AsyncIterable` + * @public + * @param groupId - The unique identifier for the `Group` resource + * @param query - OData Query {@link ODataQuery} + * @param queryMode - Mode to use while querying. Some features are only available at "advanced". + */ + async *getGroupUserMembers( + groupId: string, + query?: ODataQuery, + queryMode?: 'basic' | 'advanced', + ): AsyncIterable { + yield* this.requestCollection( + `groups/${groupId}/members/microsoft.graph.user/`, + query, + queryMode, + ); + } + /** * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/organization | Organization} * from Graph API diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts index 0f9ddb0c13..b1dfc5a148 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts @@ -61,9 +61,9 @@ function group(data: Partial): GroupEntity { describe('read microsoft graph', () => { const client: jest.Mocked = { getUsers: jest.fn(), - getUserProfile: jest.fn(), getGroups: jest.fn(), getGroupMembers: jest.fn(), + getGroupUserMembers: jest.fn(), getUserPhotoWithSizeLimit: jest.fn(), getGroupPhotoWithSizeLimit: jest.fn(), getOrganization: jest.fn(), @@ -78,13 +78,6 @@ describe('read microsoft graph', () => { mail: 'user.name@example.com', }; } - async function getExampleUserProfile() { - return { - id: 'userid', - displayName: 'User Name', - mail: 'user.name@example.com', - }; - } async function* getExampleGroups() { yield { id: 'groupid', @@ -245,9 +238,8 @@ describe('read microsoft graph', () => { describe('readMicrosoftGraphUsersInGroups', () => { it('should read users from Groups', async () => { client.getGroups.mockImplementation(getExampleGroups); - client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getGroupUserMembers.mockImplementation(getExampleUsers); - client.getUserProfile.mockResolvedValue(getExampleUserProfile()); client.getUserPhotoWithSizeLimit.mockResolvedValue( 'data:image/jpeg;base64,...', ); @@ -281,19 +273,19 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq true', + select: ['id', 'displayName'], top: 999, }, undefined, ); - expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { - top: 999, - }); - expect(client.getUserProfile).toHaveBeenCalledTimes(1); - expect(client.getUserProfile).toHaveBeenCalledWith('userid', { - expand: undefined, - }); + expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); + expect(client.getGroupUserMembers).toHaveBeenCalledWith( + 'groupid', + { top: 999 }, + undefined, + ); + expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( 'userid', @@ -303,13 +295,8 @@ describe('read microsoft graph', () => { it('should read users from Groups with advanced query mode', async () => { client.getGroups.mockImplementation(getExampleGroups); - client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getGroupUserMembers.mockImplementation(getExampleUsers); - client.getUserProfile.mockResolvedValue({ - id: 'userid', - displayName: 'User Name', - mail: 'user.name@example.com', - }); client.getUserPhotoWithSizeLimit.mockResolvedValue( 'data:image/jpeg;base64,...', ); @@ -344,19 +331,19 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'securityEnabled eq true', + select: ['id', 'displayName'], top: 999, }, 'advanced', ); - expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { - top: 999, - }); - expect(client.getUserProfile).toHaveBeenCalledTimes(1); - expect(client.getUserProfile).toHaveBeenCalledWith('userid', { - expand: undefined, - }); + expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); + expect(client.getGroupUserMembers).toHaveBeenCalledWith( + 'groupid', + { top: 999 }, + 'advanced', + ); + expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( 'userid', @@ -366,8 +353,8 @@ describe('read microsoft graph', () => { it('should read users with userExpand, groupExpand and custom transformer', async () => { client.getGroups.mockImplementation(getExampleGroups); - client.getGroupMembers.mockImplementation(getExampleGroupMembers); - client.getUserProfile.mockResolvedValue(getExampleUserProfile()); + client.getGroupUserMembers.mockImplementation(getExampleUsers); + client.getUserPhotoWithSizeLimit.mockResolvedValue( 'data:image/jpeg;base64,...', ); @@ -399,19 +386,22 @@ describe('read microsoft graph', () => { { expand: 'member', filter: 'securityEnabled eq true', + select: ['id', 'displayName'], + top: 999, + }, + undefined, + ); + + expect(client.getGroupUserMembers).toHaveBeenCalledTimes(1); + expect(client.getGroupUserMembers).toHaveBeenCalledWith( + 'groupid', + { + expand: 'manager', top: 999, }, undefined, ); - expect(client.getGroupMembers).toHaveBeenCalledTimes(1); - expect(client.getGroupMembers).toHaveBeenCalledWith('groupid', { - top: 999, - }); - expect(client.getUserProfile).toHaveBeenCalledTimes(1); - expect(client.getUserProfile).toHaveBeenCalledWith('userid', { - expand: 'manager', - }); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledWith( 'userid', @@ -1001,13 +991,13 @@ describe('read microsoft graph', () => { }); client.getUsers.mockImplementation(getExampleUsers); - client.getUserProfile.mockImplementation(getExampleUserProfile); client.getUserPhotoWithSizeLimit.mockResolvedValue( 'data:image/jpeg;base64,...', ); client.getGroups.mockImplementation(getExampleGroups); client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getGroupUserMembers.mockImplementation(getExampleUsers); client.getGroupPhotoWithSizeLimit.mockResolvedValue( 'data:image/jpeg;base64,...', ); @@ -1023,6 +1013,7 @@ describe('read microsoft graph', () => { expect(client.getGroups).toHaveBeenCalledWith( { filter: 'name eq backstage-group', + select: ['id', 'displayName'], top: 999, }, undefined, @@ -1034,7 +1025,6 @@ describe('read microsoft graph', () => { }, undefined, ); - expect(client.getUserProfile).toHaveBeenCalledTimes(1); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); }); }); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 2541416777..d0c09d6267 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -98,13 +98,7 @@ export async function readMicrosoftGraphUsers( ): Promise<{ users: UserEntity[]; // With all relations empty }> { - const users: UserEntity[] = []; - const limiter = limiterFactory(10); - - const transformer = options.transformer ?? defaultUserTransformer; - const promises: Promise[] = []; - - for await (const user of client.getUsers( + const users = client.getUsers( { filter: options.userFilter, expand: options.userExpand, @@ -112,37 +106,16 @@ export async function readMicrosoftGraphUsers( top: PAGE_SIZE, }, options.queryMode, - )) { - // Process all users in parallel, otherwise it can take quite some time - promises.push( - limiter(async () => { - let userPhoto; - try { - userPhoto = await client.getUserPhotoWithSizeLimit( - user.id!, - // We are limiting the photo size, as users with full resolution photos - // can make the Backstage API slow - 120, - ); - } catch (e) { - options.logger.warn(`Unable to load photo for ${user.id}, ${e}`); - } + ); - const entity = await transformer(user, userPhoto); - - if (!entity) { - return; - } - - users.push(entity); - }), - ); - } - - // Wait for all users and photos to be downloaded - await Promise.all(promises); - - return { users }; + return { + users: await transformUsers( + client, + users, + options.logger, + options.transformer, + ), + }; } export async function readMicrosoftGraphUsersInGroups( @@ -160,21 +133,17 @@ export async function readMicrosoftGraphUsersInGroups( ): Promise<{ users: UserEntity[]; // With all relations empty }> { - const users: UserEntity[] = []; - const limiter = limiterFactory(10); - const transformer = options.transformer ?? defaultUserTransformer; const userGroupMemberPromises: Promise[] = []; - const userPromises: Promise[] = []; - - const groupMemberUsers: Set = new Set(); + const userGroupMembers = new Map(); for await (const group of client.getGroups( { expand: options.groupExpand, search: options.userGroupMemberSearch, filter: options.userGroupMemberFilter, + select: ['id', 'displayName'], top: PAGE_SIZE, }, options.queryMode, @@ -182,17 +151,23 @@ export async function readMicrosoftGraphUsersInGroups( // Process all groups in parallel, otherwise it can take quite some time userGroupMemberPromises.push( limiter(async () => { - for await (const member of client.getGroupMembers(group.id!, { - top: PAGE_SIZE, - })) { - if (!member.id) { - continue; - } - - if (member['@odata.type'] === '#microsoft.graph.user') { - groupMemberUsers.add(member.id); - } + let groupMemberCount = 0; + for await (const user of client.getGroupUserMembers( + group.id!, + { + expand: options.userExpand, + top: PAGE_SIZE, + }, + options.queryMode, + )) { + userGroupMembers.set(user.id!, user); + groupMemberCount++; } + options.logger.debug('Read users from group', { + groupId: group.id, + groupName: group.displayName, + memberCount: groupMemberCount, + }); }), ); } @@ -200,47 +175,19 @@ export async function readMicrosoftGraphUsersInGroups( // Wait for all group members await Promise.all(userGroupMemberPromises); - options.logger.info(`groupMemberUsers ${groupMemberUsers.size}`); - for (const userId of groupMemberUsers) { - // Process all users in parallel, otherwise it can take quite some time - userPromises.push( - limiter(async () => { - let user; - let userPhoto; - try { - user = await client.getUserProfile(userId, { - expand: options.userExpand, - }); - } catch (e) { - options.logger.warn(`Unable to load user for ${userId}, ${e}`); - } - if (user) { - try { - userPhoto = await client.getUserPhotoWithSizeLimit( - user.id!, - // We are limiting the photo size, as users with full resolution photos - // can make the Backstage API slow - 120, - ); - } catch (e) { - options.logger.warn(`Unable to load userphoto for ${userId}, ${e}`); - } + options.logger.info('Read users from group membership', { + groupCount: userGroupMemberPromises.length, + userCount: userGroupMembers.size, + }); - const entity = await transformer(user, userPhoto); - - if (!entity) { - return; - } - users.push(entity); - } - }), - ); - } - - // Wait for all users and photos to be downloaded - await Promise.all(userPromises); - - return { users }; + return { + users: await transformUsers( + client, + userGroupMembers.values(), + options.logger, + options.transformer, + ), + }; } /** @@ -602,6 +549,56 @@ export async function readMicrosoftGraphOrg( return { users, groups }; } +async function transformUsers( + client: MicrosoftGraphClient, + users: Iterable | AsyncIterable, + logger: Logger, + transformer?: UserTransformer, +) { + const limiter = limiterFactory(10); + + const resolvedTransformer = transformer ?? defaultUserTransformer; + const promises: Promise[] = []; + const entities: UserEntity[] = []; + + // Process all users in parallel, otherwise it can take quite some time + for await (const user of users) { + promises.push( + limiter(async () => { + let userPhoto; + try { + userPhoto = await client.getUserPhotoWithSizeLimit( + user.id!, + // We are limiting the photo size, as users with full resolution photos + // can make the Backstage API slow + 120, + ); + } catch (e) { + logger.warn(`Unable to load user photo for`, { + user: user.id, + error: e, + }); + } + + const entity = await resolvedTransformer(user, userPhoto); + + if (entity) { + entities.push(entity); + } + }), + ); + } + + // Wait for all users and photos to be downloaded + await Promise.all(promises); + + logger.debug('Finished transforming users', { + microsoftUserCount: promises.length, + backstageUserCount: entities.length, + }); + return entities; +} + function ensureItem( target: Map>, key: string, From cbe69fa263663b93276e0f17b140208093a66732 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Jan 2023 12:05:53 +0100 Subject: [PATCH 007/130] fix formatting Signed-off-by: Patrik Oldsberg --- packages/backend-common/src/scm/git.test.ts | 6 +++--- .../src/layout/InfoCard/InfoCard.tsx | 4 ++-- packages/techdocs-cli/src/lib/PublisherConfig.ts | 4 ++-- plugins/badges-backend/src/types.ts | 2 +- plugins/bitbucket-cloud-common/src/models/index.ts | 14 +++++++------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/backend-common/src/scm/git.test.ts b/packages/backend-common/src/scm/git.test.ts index fdd3c7dbc8..629173f96e 100644 --- a/packages/backend-common/src/scm/git.test.ts +++ b/packages/backend-common/src/scm/git.test.ts @@ -202,7 +202,7 @@ describe('Git', () => { await git.clone({ url, dir }); const { onAuth } = ( - isomorphic.clone as unknown as jest.Mock + isomorphic.clone as unknown as jest.Mock<(typeof isomorphic)['clone']> ).mock.calls[0][0]!; expect(onAuth()).toEqual(auth); @@ -315,7 +315,7 @@ describe('Git', () => { await git.fetch({ remote, dir }); const { onAuth } = ( - isomorphic.fetch as unknown as jest.Mock + isomorphic.fetch as unknown as jest.Mock<(typeof isomorphic)['fetch']> ).mock.calls[0][0]!; expect(onAuth()).toEqual(auth); @@ -486,7 +486,7 @@ describe('Git', () => { await git.push({ remote, dir, remoteRef, force }); const { onAuth } = ( - isomorphic.push as unknown as jest.Mock + isomorphic.push as unknown as jest.Mock<(typeof isomorphic)['push']> ).mock.calls[0][0]!; expect(onAuth()).toEqual(auth); diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.tsx index 95d474aae4..3d8d0f3bb0 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.tsx @@ -188,12 +188,12 @@ export function InfoCard(props: Props): JSX.Element { variants.forEach(name => { calculatedStyle = { ...calculatedStyle, - ...VARIANT_STYLES.card[name as keyof typeof VARIANT_STYLES['card']], + ...VARIANT_STYLES.card[name as keyof (typeof VARIANT_STYLES)['card']], }; calculatedCardStyle = { ...calculatedCardStyle, ...VARIANT_STYLES.cardContent[ - name as keyof typeof VARIANT_STYLES['cardContent'] + name as keyof (typeof VARIANT_STYLES)['cardContent'] ], }; }); diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.ts b/packages/techdocs-cli/src/lib/PublisherConfig.ts index 0d1c8fc8a9..0ef6b66eaf 100644 --- a/packages/techdocs-cli/src/lib/PublisherConfig.ts +++ b/packages/techdocs-cli/src/lib/PublisherConfig.ts @@ -17,7 +17,7 @@ import { ConfigReader } from '@backstage/config'; import { OptionValues } from 'commander'; -type Publisher = keyof typeof PublisherConfig['configFactories']; +type Publisher = keyof (typeof PublisherConfig)['configFactories']; type PublisherConfiguration = { [p in Publisher]?: any; } & { @@ -73,7 +73,7 @@ export class PublisherConfig { */ private static isKnownPublisher( type: string, - ): type is keyof typeof PublisherConfig['configFactories'] { + ): type is keyof (typeof PublisherConfig)['configFactories'] { return PublisherConfig.configFactories.hasOwnProperty(type); } diff --git a/plugins/badges-backend/src/types.ts b/plugins/badges-backend/src/types.ts index eec7063f88..0d778289f9 100644 --- a/plugins/badges-backend/src/types.ts +++ b/plugins/badges-backend/src/types.ts @@ -27,7 +27,7 @@ export const BADGE_STYLES = [ ] as const; /** @public */ -export type BadgeStyle = typeof BADGE_STYLES[number]; +export type BadgeStyle = (typeof BADGE_STYLES)[number]; /** @public */ export interface Badge { diff --git a/plugins/bitbucket-cloud-common/src/models/index.ts b/plugins/bitbucket-cloud-common/src/models/index.ts index 9d25bbb873..d868e2926c 100644 --- a/plugins/bitbucket-cloud-common/src/models/index.ts +++ b/plugins/bitbucket-cloud-common/src/models/index.ts @@ -107,7 +107,7 @@ export namespace Models { * @public */ export type BaseCommitSummaryMarkupEnum = - typeof BaseCommitSummaryMarkupEnum[keyof typeof BaseCommitSummaryMarkupEnum]; + (typeof BaseCommitSummaryMarkupEnum)[keyof typeof BaseCommitSummaryMarkupEnum]; /** * A branch object, representing a branch in a repository. @@ -146,7 +146,7 @@ export namespace Models { * @public */ export type BranchMergeStrategiesEnum = - typeof BranchMergeStrategiesEnum[keyof typeof BranchMergeStrategiesEnum]; + (typeof BranchMergeStrategiesEnum)[keyof typeof BranchMergeStrategiesEnum]; /** * A repository commit object. @@ -191,7 +191,7 @@ export namespace Models { * @public */ export type CommitFileAttributesEnum = - typeof CommitFileAttributesEnum[keyof typeof CommitFileAttributesEnum]; + (typeof CommitFileAttributesEnum)[keyof typeof CommitFileAttributesEnum]; /** * A link to a resource related to this object. @@ -280,7 +280,7 @@ export namespace Models { * @public */ export type ParticipantRoleEnum = - typeof ParticipantRoleEnum[keyof typeof ParticipantRoleEnum]; + (typeof ParticipantRoleEnum)[keyof typeof ParticipantRoleEnum]; /** * @public @@ -295,7 +295,7 @@ export namespace Models { * @public */ export type ParticipantStateEnum = - typeof ParticipantStateEnum[keyof typeof ParticipantStateEnum]; + (typeof ParticipantStateEnum)[keyof typeof ParticipantStateEnum]; /** * A Bitbucket project. @@ -423,7 +423,7 @@ export namespace Models { * @public */ export type RepositoryForkPolicyEnum = - typeof RepositoryForkPolicyEnum[keyof typeof RepositoryForkPolicyEnum]; + (typeof RepositoryForkPolicyEnum)[keyof typeof RepositoryForkPolicyEnum]; /** * @public @@ -436,7 +436,7 @@ export namespace Models { * @public */ export type RepositoryScmEnum = - typeof RepositoryScmEnum[keyof typeof RepositoryScmEnum]; + (typeof RepositoryScmEnum)[keyof typeof RepositoryScmEnum]; /** * @public From 49948f644f1276f19020171a223fa74fbde6d1fd Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Sat, 28 Jan 2023 01:49:58 -0500 Subject: [PATCH 008/130] Standardize term 'gitlab instance' In other places in the docs the term 'instance' is used to refer to a single GitLab API host. The use of 'project' in these cases is particularly troublesome because that term already has a well-defined meaning in GitLab: a single codebase with associated issues, MRs, and automation. Signed-off-by: Jamie Klassen --- .changeset/breezy-ears-rule.md | 5 +++++ docs/integrations/gitlab/discovery.md | 2 +- plugins/catalog-backend-module-gitlab/config.d.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/breezy-ears-rule.md diff --git a/.changeset/breezy-ears-rule.md b/.changeset/breezy-ears-rule.md new file mode 100644 index 0000000000..753554187d --- /dev/null +++ b/.changeset/breezy-ears-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +The config now consistently uses the term 'instance' to refer to a single GitLab API host. diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index dc0b911b3d..51c34b67ac 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -22,7 +22,7 @@ catalog: yourProviderId: host: gitlab-host # Identifies one of the hosts set up in the integrations branch: main # Optional. Uses `master` as default - group: example-group # Optional. Group and subgroup (if needed) to look for repositories. If not present the whole project will be scanned + group: example-group # Optional. Group and subgroup (if needed) to look for repositories. If not present the whole instance will be scanned entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` projectPattern: /[\s\S]*/ # Optional. Filters found projects based on provided patter. Defaults to `/[\s\S]*/`, what means to not filter anything schedule: # optional; same options as in TaskScheduleDefinition diff --git a/plugins/catalog-backend-module-gitlab/config.d.ts b/plugins/catalog-backend-module-gitlab/config.d.ts index 4f51867efc..00ad0ef032 100644 --- a/plugins/catalog-backend-module-gitlab/config.d.ts +++ b/plugins/catalog-backend-module-gitlab/config.d.ts @@ -31,7 +31,7 @@ export interface Config { host: string; /** * (Optional) Gitlab's group[/subgroup] where the discovery is done. - * If not defined the whole project will be scanned. + * If not defined the whole instance will be scanned. */ group?: string; /** From c5957024a3dc0ced76a9c2c98ed31136f0ca35c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 16:41:23 +0000 Subject: [PATCH 009/130] chore(deps): update backstage/actions action to v0.6.0 Signed-off-by: Renovate Bot --- .github/workflows/automate_changeset_feedback.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/cron.yml | 2 +- .github/workflows/deploy_docker-image.yml | 2 +- .github/workflows/deploy_nightly.yml | 2 +- .github/workflows/deploy_packages.yml | 4 ++-- .github/workflows/issue.yaml | 2 +- .github/workflows/pr-review-comment.yaml | 2 +- .github/workflows/pr.yaml | 2 +- .github/workflows/sync_code-formatting.yml | 2 +- .github/workflows/sync_snyk-github-issues.yml | 2 +- .github/workflows/uffizzi-build.yml | 2 +- .github/workflows/verify_e2e-linux.yml | 2 +- .github/workflows/verify_kubernetes.yml | 2 +- .github/workflows/verify_storybook.yml | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index e18aa9d428..11fef75b9b 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -28,7 +28,7 @@ jobs: ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' - name: fetch base run: git fetch --depth 1 origin ${{ github.base_ref }} - - uses: backstage/actions/changeset-feedback@v0.5.13 + - uses: backstage/actions/changeset-feedback@v0.6.0 name: Generate feedback with: diffRef: 'origin/master' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10db591f4c..16573cf5b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -63,7 +63,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -181,7 +181,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index dc221c1a1e..2e6573ede8 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,7 +8,7 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.5.13 + - uses: backstage/actions/cron@v0.6.0 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index d8470d4124..909a36199c 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -26,7 +26,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index 5f337ace3e..c9cd22640c 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -26,7 +26,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index c05b850883..fa43271a96 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -68,7 +68,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -145,7 +145,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index d16d0b55d7..9f2e5827be 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -10,4 +10,4 @@ jobs: if: github.repository == 'backstage/backstage' steps: - name: Issue sync - uses: backstage/actions/issue-sync@v0.5.13 + uses: backstage/actions/issue-sync@v0.6.0 diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index 11b590809a..261ce56177 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -35,7 +35,7 @@ jobs: const prNumber = artifact.name.slice('pr_number-'.length) core.setOutput('pr-number', prNumber); - - uses: backstage/actions/re-review@v0.5.13 + - uses: backstage/actions/re-review@v0.6.0 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index acbd11aece..1cde52bfbb 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,7 +18,7 @@ jobs: if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request ) steps: - name: PR sync - uses: backstage/actions/pr-sync@v0.5.13 + uses: backstage/actions/pr-sync@v0.6.0 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 19cc5f8045..b84fe5c2e6 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -20,7 +20,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index 9dcc9f63ef..dfd114c66a 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -23,7 +23,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index a23216a9cb..968bc859d6 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -27,7 +27,7 @@ jobs: registry-url: https://registry.npmjs.org/ - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: linux-v16 diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index f687e4f5a4..16ddb04086 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -52,7 +52,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_kubernetes.yml b/.github/workflows/verify_kubernetes.yml index 7fbcf9fa3f..6d8a7682b5 100644 --- a/.github/workflows/verify_kubernetes.yml +++ b/.github/workflows/verify_kubernetes.yml @@ -28,7 +28,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 0e63393af8..38b685c539 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -35,7 +35,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.13 + uses: backstage/actions/yarn-install@v0.6.0 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: storybook yarn install From 6197146926d9aa7a39eb7173fb5f71b5d292529a Mon Sep 17 00:00:00 2001 From: Alex Crome Date: Tue, 31 Jan 2023 18:56:20 +0000 Subject: [PATCH 010/130] Fix bad merge Signed-off-by: Alex Crome --- .../catalog-backend-module-msgraph/src/microsoftGraph/read.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index a060df5ea3..003448a3e8 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -38,6 +38,7 @@ import { defaultOrganizationTransformer, defaultUserTransformer, } from './defaultTransformers'; +import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; const PAGE_SIZE = 999; From 77c41b69241da6c266c9f51e67c755ebe45df756 Mon Sep 17 00:00:00 2001 From: Damon Kaswell Date: Tue, 31 Jan 2023 11:13:06 -0800 Subject: [PATCH 011/130] Added changeset Signed-off-by: Damon Kaswell --- .changeset/tall-tables-hide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tall-tables-hide.md diff --git a/.changeset/tall-tables-hide.md b/.changeset/tall-tables-hide.md new file mode 100644 index 0000000000..3e8011aff2 --- /dev/null +++ b/.changeset/tall-tables-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Updated README to include newer API options for incremental entity providers From 72a06103d01cc0d16b2ca87664624f9f17ede9de Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:09:35 -0500 Subject: [PATCH 012/130] add trpc widget Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .../ApiDefinitionCard/ApiDefinitionWidget.tsx | 8 ++++ .../GraphQlDefinition.tsx | 2 +- .../TrpcApiDefinitionWidget.test.tsx | 32 +++++++++++++++ .../TrpcApiDefinitionWidget.tsx | 40 +++++++++++++++++++ .../components/TrpcDefinitionWidget/index.ts | 19 +++++++++ plugins/api-docs/src/components/index.ts | 1 + 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.test.tsx create mode 100644 plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx create mode 100644 plugins/api-docs/src/components/TrpcDefinitionWidget/index.ts diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionWidget.tsx b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionWidget.tsx index 03a56eaa78..8d7d7a7729 100644 --- a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionWidget.tsx @@ -18,6 +18,7 @@ import { AsyncApiDefinitionWidget } from '../AsyncApiDefinitionWidget'; import { GraphQlDefinitionWidget } from '../GraphQlDefinitionWidget'; import { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget'; import { GrpcApiDefinitionWidget } from '../GrpcApiDefinitionWidget'; +import { TrpcApiDefinitionWidget } from '../TrpcDefinitionWidget'; /** @public */ export type ApiDefinitionWidget = { @@ -61,5 +62,12 @@ export function defaultDefinitionWidgets(): ApiDefinitionWidget[] { ), }, + { + type: 'trpc', + title: 'tRPC', + component: definition => ( + + ), + }, ]; } diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx index d3fddf70cf..3dbe941be3 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx @@ -44,7 +44,7 @@ type Props = { definition: string; }; -export const GraphQlDefinition = ({ definition }: Props) => { +export const GraphQlDefinition: React.FC = ({ definition }: Props) => { const classes = useStyles(); const schema = buildSchema(definition); diff --git a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.test.tsx new file mode 100644 index 0000000000..d7e6951e2e --- /dev/null +++ b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.test.tsx @@ -0,0 +1,32 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { TrpcApiDefinitionWidget } from './TrpcApiDefinitionWidget'; + +describe('', () => { + it('renders plain text', async () => { + const { getAllByText } = await renderInTestApp( + , + ); + + expect( + getAllByText((_text, element) => element?.textContent === 'Hello World') + .length, + ).toBeGreaterThan(0); + }); +}); diff --git a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx new file mode 100644 index 0000000000..c4cf56bedb --- /dev/null +++ b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { CodeSnippet } from '@backstage/core-components'; +import { useTheme } from '@material-ui/core/styles'; +import { BackstageTheme } from '@backstage/theme'; + +/** @public */ +export type TrpcApiDefinitionWidgetProps = { + definition: string; +}; + +/** @public */ +export const TrpcApiDefinitionWidget: React.FC = ( + props: TrpcApiDefinitionWidgetProps, +) => { + const { definition } = props; + const theme = useTheme(); + return ( + + ); +}; diff --git a/plugins/api-docs/src/components/TrpcDefinitionWidget/index.ts b/plugins/api-docs/src/components/TrpcDefinitionWidget/index.ts new file mode 100644 index 0000000000..604adb06f3 --- /dev/null +++ b/plugins/api-docs/src/components/TrpcDefinitionWidget/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 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 { + TrpcApiDefinitionWidget, + type TrpcApiDefinitionWidgetProps, +} from './TrpcApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/index.ts b/plugins/api-docs/src/components/index.ts index ea40076af4..7e7b206c54 100644 --- a/plugins/api-docs/src/components/index.ts +++ b/plugins/api-docs/src/components/index.ts @@ -22,3 +22,4 @@ export * from './ComponentsCards'; export * from './GraphQlDefinitionWidget'; export * from './OpenApiDefinitionWidget'; export * from './PlainApiDefinitionWidget'; +export * from './TrpcDefinitionWidget'; From 9ed53218a5395667523e2ed9dfd50d5ab655b767 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:21:13 -0500 Subject: [PATCH 013/130] add changeset for api-docs plugins Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .changeset/great-goats-talk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/great-goats-talk.md diff --git a/.changeset/great-goats-talk.md b/.changeset/great-goats-talk.md new file mode 100644 index 0000000000..92a1cd59ee --- /dev/null +++ b/.changeset/great-goats-talk.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': minor +--- + +Adds a new tRPC API definition widget which users can utilize to integrate their tRPC API definitions into Backstage. From ed40fbbe7e48eb3a6d51ec6da436eefd5ee37c40 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:42:05 -0500 Subject: [PATCH 014/130] add tRPC to dev app Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/api-docs/dev/index.tsx | 15 +++++++++++++ plugins/api-docs/dev/trpc-example-api.yaml | 25 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 plugins/api-docs/dev/trpc-example-api.yaml diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx index 9975e91e98..995aa9c2fb 100644 --- a/plugins/api-docs/dev/index.tsx +++ b/plugins/api-docs/dev/index.tsx @@ -32,6 +32,7 @@ import graphqlApiEntity from './graphql-example-api.yaml'; import invalidLanguageApiEntity from './invalid-language-example-api.yaml'; import openapiApiEntity from './openapi-example-api.yaml'; import otherApiEntity from './other-example-api.yaml'; +import trpcApiEntity from './trpc-example-api.yaml'; const mockEntities = [ openapiApiEntity, @@ -39,6 +40,7 @@ const mockEntities = [ graphqlApiEntity, invalidLanguageApiEntity, otherApiEntity, + trpcApiEntity, ] as unknown as Entity[]; createDevApp() @@ -137,4 +139,17 @@ createDevApp() ), }) + .addPage({ + title: 'tRPC', + element: ( + +
+ + + + + + + ), + }) .render(); diff --git a/plugins/api-docs/dev/trpc-example-api.yaml b/plugins/api-docs/dev/trpc-example-api.yaml new file mode 100644 index 0000000000..4adf78835c --- /dev/null +++ b/plugins/api-docs/dev/trpc-example-api.yaml @@ -0,0 +1,25 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world + description: Hello World example for tRPC +spec: + type: trpc + lifecycle: experimental + owner: team-c + definition: | + import { z } from 'zod'; + import { publicProcedure, router } from '../trpc'; + + export const apiRouter = router({ + version: publicProcedure.query(() => { + return { version: '0.42.0' }; + }), + hello: publicProcedure + .input(z.object({ username: z.string().nullish() }).nullish()) + .query(({ input, ctx }) => { + return { + text: `hello ${input?.username ?? ctx.user?.name ?? 'world'}`, + }; + }), + }); From a2194b2a4d0415b27d49dcaac5b916462d6dcc79 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:47:51 -0500 Subject: [PATCH 015/130] rename trpc-sample api Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/api-docs/dev/trpc-example-api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api-docs/dev/trpc-example-api.yaml b/plugins/api-docs/dev/trpc-example-api.yaml index 4adf78835c..eff7efa8f1 100644 --- a/plugins/api-docs/dev/trpc-example-api.yaml +++ b/plugins/api-docs/dev/trpc-example-api.yaml @@ -1,7 +1,7 @@ apiVersion: backstage.io/v1alpha1 kind: API metadata: - name: hello-world + name: hello-world-trpc description: Hello World example for tRPC spec: type: trpc From a16f81df082b8e89b323c71d6d70c80e3e788c52 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Tue, 31 Jan 2023 17:06:16 -0500 Subject: [PATCH 016/130] add the tRPC hello world example Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .../examples/apis/hello-world-trpc-api.yaml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/catalog-model/examples/apis/hello-world-trpc-api.yaml diff --git a/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml b/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml new file mode 100644 index 0000000000..eff7efa8f1 --- /dev/null +++ b/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml @@ -0,0 +1,25 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world-trpc + description: Hello World example for tRPC +spec: + type: trpc + lifecycle: experimental + owner: team-c + definition: | + import { z } from 'zod'; + import { publicProcedure, router } from '../trpc'; + + export const apiRouter = router({ + version: publicProcedure.query(() => { + return { version: '0.42.0' }; + }), + hello: publicProcedure + .input(z.object({ username: z.string().nullish() }).nullish()) + .query(({ input, ctx }) => { + return { + text: `hello ${input?.username ?? ctx.user?.name ?? 'world'}`, + }; + }), + }); From d2e1364c981dd94d9aaceee3af36d82aab12e5f2 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:09:07 -0500 Subject: [PATCH 017/130] add target to fix all prettier issues Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- package.json | 1 + packages/catalog-model/examples/apis/hello-world-trpc-api.yaml | 2 +- plugins/api-docs/dev/trpc-example-api.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 912dd53b8a..e08ca3adfd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "create-plugin": "echo \"use 'yarn new' instead\"", "release": "node scripts/prepare-release.js && changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}' '.changeset/*.json' && yarn install --no-immutable", "prettier:check": "prettier --check .", + "prettier:fix": "prettier --write .", "storybook": "yarn ./storybook run start", "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", "snyk:test:package": "yarn snyk:test --include", diff --git a/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml b/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml index eff7efa8f1..f5807537b3 100644 --- a/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml +++ b/packages/catalog-model/examples/apis/hello-world-trpc-api.yaml @@ -10,7 +10,7 @@ spec: definition: | import { z } from 'zod'; import { publicProcedure, router } from '../trpc'; - + export const apiRouter = router({ version: publicProcedure.query(() => { return { version: '0.42.0' }; diff --git a/plugins/api-docs/dev/trpc-example-api.yaml b/plugins/api-docs/dev/trpc-example-api.yaml index eff7efa8f1..f5807537b3 100644 --- a/plugins/api-docs/dev/trpc-example-api.yaml +++ b/plugins/api-docs/dev/trpc-example-api.yaml @@ -10,7 +10,7 @@ spec: definition: | import { z } from 'zod'; import { publicProcedure, router } from '../trpc'; - + export const apiRouter = router({ version: publicProcedure.query(() => { return { version: '0.42.0' }; From 60a466094c1478f797ab569479a1897c89d98581 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:39:43 -0500 Subject: [PATCH 018/130] update api-report.md Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/api-docs/api-report.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index 0872d97355..070e950202 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -173,4 +173,12 @@ export const ProvidedApisCard: (props: { export const ProvidingComponentsCard: (props: { variant?: InfoCardVariants; }) => JSX.Element; + +// @public (undocumented) +export const TrpcApiDefinitionWidget: React_2.FC; + +// @public (undocumented) +export type TrpcApiDefinitionWidgetProps = { + definition: string; +}; ``` From ec34b535c044ce97f60ce422ecfcd315a43be3d9 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Wed, 1 Feb 2023 14:23:32 -0500 Subject: [PATCH 019/130] feat(AdrSearchResultListItem): support displaying icon & hook up extensions Signed-off-by: Phil Kuang --- .changeset/nine-ravens-kiss.md | 5 +++ .changeset/proud-glasses-live.md | 5 +++ plugins/adr/api-report.md | 13 +++++-- plugins/adr/package.json | 1 + plugins/adr/src/index.ts | 4 +-- plugins/adr/src/plugin.ts | 20 +++++++++++ .../src/search/AdrSearchResultListItem.tsx | 35 ++++++++++--------- plugins/adr/src/search/index.ts | 2 +- yarn.lock | 1 + 9 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 .changeset/nine-ravens-kiss.md create mode 100644 .changeset/proud-glasses-live.md diff --git a/.changeset/nine-ravens-kiss.md b/.changeset/nine-ravens-kiss.md new file mode 100644 index 0000000000..a507db6b03 --- /dev/null +++ b/.changeset/nine-ravens-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr': minor +--- + +The `AdrSearchResultListItem` component is now a search result extension. This means that when rendered as a child of components that render search extensions, the `result`, `rank`, and `highlight` properties are optional. See the [documentation](https://backstage.io/docs/features/search/how-to-guides#how-to-render-search-results-using-extensions) for more details. diff --git a/.changeset/proud-glasses-live.md b/.changeset/proud-glasses-live.md new file mode 100644 index 0000000000..ded7a53ce7 --- /dev/null +++ b/.changeset/proud-glasses-live.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr': patch +--- + +Support displaying an icon on `AdrSearchResultListItem` diff --git a/plugins/adr/api-report.md b/plugins/adr/api-report.md index d6480cd090..4ced4e9416 100644 --- a/plugins/adr/api-report.md +++ b/plugins/adr/api-report.md @@ -11,6 +11,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { isAdrAvailable } from '@backstage/plugin-adr-common'; +import { ReactNode } from 'react'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -82,12 +83,18 @@ export type AdrReadResult = { }; // @public -export function AdrSearchResultListItem(props: { +export const AdrSearchResultListItem: ( + props: AdrSearchResultListItemProps, +) => JSX.Element | null; + +// @public (undocumented) +export type AdrSearchResultListItemProps = { lineClamp?: number; highlight?: ResultHighlight; + icon?: ReactNode; rank?: number; - result: AdrDocument; -}): JSX.Element; + result?: AdrDocument; +}; // @public export const EntityAdrContent: (props: { diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 9d915b7af7..5f3d3ba12a 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -39,6 +39,7 @@ "remark-gfm": "^3.0.1" }, "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", "react": "^16.13.1 || ^17.0.0", "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, diff --git a/plugins/adr/src/index.ts b/plugins/adr/src/index.ts index 2ec76b0fd6..f24b3ac531 100644 --- a/plugins/adr/src/index.ts +++ b/plugins/adr/src/index.ts @@ -30,5 +30,5 @@ export type { } from './api'; export { isAdrAvailable } from '@backstage/plugin-adr-common'; export * from './components/AdrReader'; -export { adrPlugin, EntityAdrContent } from './plugin'; -export * from './search'; +export { adrPlugin, AdrSearchResultListItem, EntityAdrContent } from './plugin'; +export type { AdrSearchResultListItemProps } from './search'; diff --git a/plugins/adr/src/plugin.ts b/plugins/adr/src/plugin.ts index f23df826cd..05e4adec4a 100644 --- a/plugins/adr/src/plugin.ts +++ b/plugins/adr/src/plugin.ts @@ -21,7 +21,9 @@ import { createRoutableExtension, discoveryApiRef, } from '@backstage/core-plugin-api'; +import { createSearchResultListItemExtension } from '@backstage/plugin-search-react'; import { rootRouteRef } from './routes'; +import { AdrSearchResultListItemProps } from './search'; /** * The Backstage plugin that holds ADR specific components @@ -57,3 +59,21 @@ export const EntityAdrContent = adrPlugin.provide( mountPoint: rootRouteRef, }), ); + +/** + * React extension used to render results on Search page or modal + * + * @public + */ +export const AdrSearchResultListItem: ( + props: AdrSearchResultListItemProps, +) => JSX.Element | null = adrPlugin.provide( + createSearchResultListItemExtension({ + name: 'AdrSearchResultListItem', + component: () => + import('./search/AdrSearchResultListItem').then( + m => m.AdrSearchResultListItem, + ), + predicate: result => result.type === 'adr', + }), +); diff --git a/plugins/adr/src/search/AdrSearchResultListItem.tsx b/plugins/adr/src/search/AdrSearchResultListItem.tsx index 3a11b5cf9a..118333a439 100644 --- a/plugins/adr/src/search/AdrSearchResultListItem.tsx +++ b/plugins/adr/src/search/AdrSearchResultListItem.tsx @@ -14,19 +14,19 @@ * limitations under the License. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import { Box, Chip, Divider, ListItem, + ListItemIcon, ListItemText, makeStyles, } from '@material-ui/core'; import Typography from '@material-ui/core/Typography'; import { parseEntityRef } from '@backstage/catalog-model'; import { Link } from '@backstage/core-components'; -import { useAnalytics } from '@backstage/core-plugin-api'; import { AdrDocument } from '@backstage/plugin-adr-common'; import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; import { ResultHighlight } from '@backstage/plugin-search-common'; @@ -43,35 +43,36 @@ const useStyles = makeStyles({ }, }); +/** + * @public + */ +export type AdrSearchResultListItemProps = { + lineClamp?: number; + highlight?: ResultHighlight; + icon?: ReactNode; + rank?: number; + result?: AdrDocument; +}; + /** * A component to display an ADR search result. * @public */ -export function AdrSearchResultListItem(props: { - lineClamp?: number; - highlight?: ResultHighlight; - rank?: number; - result: AdrDocument; -}) { - const { lineClamp = 5, highlight, rank, result } = props; +export function AdrSearchResultListItem(props: AdrSearchResultListItemProps) { + const { lineClamp = 5, highlight, icon, result } = props; const classes = useStyles(); - const analytics = useAnalytics(); - const handleClick = () => { - analytics.captureEvent('discover', result.title, { - attributes: { to: result.location }, - value: rank, - }); - }; + if (!result) return null; return ( <> + {icon && {icon}} + {highlight?.fields.title ? ( Date: Wed, 1 Feb 2023 15:54:12 -0500 Subject: [PATCH 020/130] add trpc example to development build, ensure syntax highlighting works Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- packages/catalog-model/examples/all-apis.yaml | 1 + .../components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/catalog-model/examples/all-apis.yaml b/packages/catalog-model/examples/all-apis.yaml index e23f1b3656..472897bfac 100644 --- a/packages/catalog-model/examples/all-apis.yaml +++ b/packages/catalog-model/examples/all-apis.yaml @@ -6,6 +6,7 @@ metadata: spec: targets: - ./apis/hello-world-api.yaml + - ./apis/hello-world-trpc-api.yaml - ./apis/petstore-api.yaml - ./apis/spotify-api.yaml - ./apis/streetlights-api.yaml diff --git a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx index c4cf56bedb..81bec09769 100644 --- a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx @@ -33,7 +33,7 @@ export const TrpcApiDefinitionWidget: React.FC = ( ); From 060713cab7b1a750ad92f3ccffe305c49b9cbd67 Mon Sep 17 00:00:00 2001 From: Callum Linington Date: Thu, 2 Feb 2023 10:39:59 +0000 Subject: [PATCH 021/130] docs(azure): add note around token usage Signed-off-by: Callum Linington --- docs/integrations/azure/locations.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/integrations/azure/locations.md b/docs/integrations/azure/locations.md index 3b3e3dd5ed..93a7264af9 100644 --- a/docs/integrations/azure/locations.md +++ b/docs/integrations/azure/locations.md @@ -28,3 +28,5 @@ The configuration is a structure with two elements: - `host`: The DevOps host; only `dev.azure.com` is supported. - `token` (optional): A personal access token as expected by Azure DevOps. + +> Note: The token should just be provided as the raw token generated by Azure DevOps using the format `:{raw_token}` \ No newline at end of file From 6856ab98f4e2d3ecbbd317a0dba8867addef583f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 12:15:00 +0000 Subject: [PATCH 022/130] Update dependency @asyncapi/react-component to v1.0.0-next.47 Signed-off-by: Renovate Bot --- .changeset/renovate-53e8b07.md | 5 +++ plugins/api-docs/package.json | 2 +- yarn.lock | 67 +++++++++++++++++----------------- 3 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 .changeset/renovate-53e8b07.md diff --git a/.changeset/renovate-53e8b07.md b/.changeset/renovate-53e8b07.md new file mode 100644 index 0000000000..cb19d8fe8b --- /dev/null +++ b/.changeset/renovate-53e8b07.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Updated dependency `@asyncapi/react-component` to `1.0.0-next.47`. diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index e6dd3d6e1f..a23a123dc3 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -32,7 +32,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@asyncapi/react-component": "1.0.0-next.44", + "@asyncapi/react-component": "1.0.0-next.47", "@backstage/catalog-model": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 4e56cb9839..b04785483a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -217,14 +217,16 @@ __metadata: languageName: node linkType: hard -"@asyncapi/avro-schema-parser@npm:^0.3.0": - version: 0.3.0 - resolution: "@asyncapi/avro-schema-parser@npm:0.3.0" - checksum: 3086bc16a634600fe4d7c83f52f9558684c0d79193958fcb9275cacfb550700eaa718aa9ed1d3c89d1d77253f67e0f33ea91e137ef08aa722d67ded9373bf142 +"@asyncapi/avro-schema-parser@npm:^1.1.0": + version: 1.1.0 + resolution: "@asyncapi/avro-schema-parser@npm:1.1.0" + dependencies: + avsc: ^5.7.3 + checksum: 31bb6285a49f255e809088164ac4638103b2ec3e761c9c85be116af8abd9fe429626ba79ba6b3899d3823a77216741d9d793841e600145f2956328f3ce45a0e0 languageName: node linkType: hard -"@asyncapi/openapi-schema-parser@npm:^2.0.0": +"@asyncapi/openapi-schema-parser@npm:^2.0.1": version: 2.0.1 resolution: "@asyncapi/openapi-schema-parser@npm:2.0.1" dependencies: @@ -233,12 +235,12 @@ __metadata: languageName: node linkType: hard -"@asyncapi/parser@npm:^1.17.0": - version: 1.17.0 - resolution: "@asyncapi/parser@npm:1.17.0" +"@asyncapi/parser@npm:^1.18.0": + version: 1.18.0 + resolution: "@asyncapi/parser@npm:1.18.0" dependencies: "@apidevtools/json-schema-ref-parser": ^9.0.6 - "@asyncapi/specs": ^3.2.0 + "@asyncapi/specs": ^4.1.0 "@fmvilas/pseudo-yaml-ast": ^0.3.1 ajv: ^6.10.1 js-yaml: ^3.13.1 @@ -246,34 +248,35 @@ __metadata: lodash.clonedeep: ^4.5.0 node-fetch: ^2.6.0 tiny-merge-patch: ^0.1.2 - checksum: 6ac0bd3c9c90abf331cfdfb054dab047cf0324ee803633c2abc6dad96b12c64d033e4fb94907906adfb521ba16e9b23083b2a6acc7719ccd22154a7867046a95 + checksum: 7e08cae52990ce97299170faf99aaf27a8e2e02e6b03df28518d256f5687f394c26fd0a4aba235514ed4e3c2fd081a79321a489c20987ed22122afdb0b9ea0dd languageName: node linkType: hard -"@asyncapi/react-component@npm:1.0.0-next.44": - version: 1.0.0-next.44 - resolution: "@asyncapi/react-component@npm:1.0.0-next.44" +"@asyncapi/react-component@npm:1.0.0-next.47": + version: 1.0.0-next.47 + resolution: "@asyncapi/react-component@npm:1.0.0-next.47" dependencies: - "@asyncapi/avro-schema-parser": ^0.3.0 - "@asyncapi/openapi-schema-parser": ^2.0.0 - "@asyncapi/parser": ^1.17.0 + "@asyncapi/avro-schema-parser": ^1.1.0 + "@asyncapi/openapi-schema-parser": ^2.0.1 + "@asyncapi/parser": ^1.18.0 highlight.js: ^10.7.2 isomorphic-dompurify: ^0.13.0 marked: ^4.0.14 openapi-sampler: ^1.2.1 - react-icons: ^4.4.0 use-resize-observer: ^8.0.0 peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 1d96db3bc6a4655884969a7639467836376c028df1a00f8930adf4e1b6ecb43e6a2d61df5c68e63d6c105d319fecb3f2ac0e4146274a81a874fbde17f4dbac1c + checksum: 837b6378f1188945a0e92d93efe71691a8dad120fad5624b58b325ec86c51435b874464ec0c47308ffacda9ee4d77ad8217881584bd74081f6db2211f09cc60c languageName: node linkType: hard -"@asyncapi/specs@npm:^3.2.0": - version: 3.2.0 - resolution: "@asyncapi/specs@npm:3.2.0" - checksum: 09971262aefc8844ab3e7c0c3652711862ac562dd5d614f23b496185690430a81df8e50eddba657f4141e0fd9548ef622fe6c20f4e3dec8054be23f774798335 +"@asyncapi/specs@npm:^4.1.0": + version: 4.1.0 + resolution: "@asyncapi/specs@npm:4.1.0" + dependencies: + "@types/json-schema": ^7.0.11 + checksum: 6e95f3c1ef7267480cdfc69f5a015f63b9101874289e31843a629346a3ea07490e3043b296a089bf3458e877c664d83d4b4738dcb53d37d7e13b75c7bc08c879 languageName: node linkType: hard @@ -4401,7 +4404,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-api-docs@workspace:plugins/api-docs" dependencies: - "@asyncapi/react-component": 1.0.0-next.44 + "@asyncapi/react-component": 1.0.0-next.47 "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" @@ -14751,7 +14754,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.11 resolution: "@types/json-schema@npm:7.0.11" checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d @@ -17172,6 +17175,13 @@ __metadata: languageName: node linkType: hard +"avsc@npm:^5.7.3": + version: 5.7.7 + resolution: "avsc@npm:5.7.7" + checksum: e3361aa88a61397b3345876263f79c8c8bfe013d849142202758205459a37e24cdbf02edc49ae019d6e82d93bbc7bc73e9e7fefca049aae91626bae28de4d1a9 + languageName: node + linkType: hard + "aws-lambda@npm:^1.0.7": version: 1.0.7 resolution: "aws-lambda@npm:1.0.7" @@ -33270,15 +33280,6 @@ __metadata: languageName: node linkType: hard -"react-icons@npm:^4.4.0": - version: 4.4.0 - resolution: "react-icons@npm:4.4.0" - peerDependencies: - react: "*" - checksum: dd93a1dcc8ac8a3cf46a2b33e80b586fa967331fa5fcb90d061abf276155a8363a331643e203d67cb9bab4261d00a757da854bcce1fce2c6e4258bf3e33f711b - languageName: node - linkType: hard - "react-immutable-proptypes@npm:2.2.0": version: 2.2.0 resolution: "react-immutable-proptypes@npm:2.2.0" From 192f68d53c79a357197eabbc80e07e32d01ef749 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:16:57 -0500 Subject: [PATCH 023/130] add trpc to api catalog Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .../catalog-model/src/schema/kinds/API.v1alpha1.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-model/src/schema/kinds/API.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/API.v1alpha1.schema.json index fae1225764..3d5af710e0 100644 --- a/packages/catalog-model/src/schema/kinds/API.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/API.v1alpha1.schema.json @@ -46,7 +46,7 @@ "type": { "type": "string", "description": "The type of the API definition.", - "examples": ["openapi", "asyncapi", "graphql", "grpc"], + "examples": ["openapi", "asyncapi", "graphql", "grpc", "trpc"], "minLength": 1 }, "lifecycle": { From 90bcf686a637ae275935ee636e49c2d51bc90dc5 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 16:13:06 +0100 Subject: [PATCH 024/130] chore: use the maintainers group Signed-off-by: blam --- .github/CODEOWNERS | 150 ++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 82d868a408..7545302d93 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,78 +4,78 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @backstage/reviewers -yarn.lock @backstage/reviewers @backstage-service -*/yarn.lock @backstage/reviewers @backstage-service -/.changeset/cost-insights-* @backstage/reviewers @backstage/silver-lining -/.changeset @backstage/reviewers @backstage/techdocs-core -/cypress/src/integration/plugins/techdocs.spec.ts @backstage/reviewers @backstage/techdocs-core -/docs/assets/search @backstage/reviewers @backstage/techdocs-core -/docs/features/search @backstage/reviewers @backstage/techdocs-core -/docs/features/techdocs @backstage/reviewers @backstage/techdocs-core -/docs/plugins/integrating-search-into-plugins.md @backstage/reviewers @backstage/techdocs-core -/packages/techdocs-cli @backstage/reviewers @backstage/techdocs-core -/packages/techdocs-cli-embedded-app @backstage/reviewers @backstage/techdocs-core -/plugins/adr @backstage/reviewers @kuangp -/plugins/adr-* @backstage/reviewers @kuangp -/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps -/plugins/apache-airflow @backstage/reviewers @cmpadden -/plugins/api-docs @backstage/reviewers @backstage/sda-se-reviewers -/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin -/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin -/plugins/bitbucket-cloud-common @backstage/reviewers @pjungermann -/plugins/bitrise @backstage/reviewers @backstage/sda-se-reviewers -/plugins/catalog @backstage/reviewers @backstage/catalog-core -/plugins/catalog-* @backstage/reviewers @backstage/catalog-core -/plugins/catalog-backend-module-aws @backstage/reviewers @backstage/catalog-core @pjungermann -/plugins/catalog-backend-module-bitbucket-cloud @backstage/reviewers @backstage/catalog-core @pjungermann -/plugins/catalog-backend-module-msgraph @backstage/reviewers @backstage/catalog-core @pjungermann -/plugins/catalog-graph @backstage/reviewers @backstage/catalog-core @backstage/sda-se-reviewers -/plugins/circleci @backstage/reviewers @adamdmharvey -/plugins/cloudbuild @backstage/reviewers @trivago/ebarrios -/plugins/code-coverage @backstage/reviewers @alde @nissayeva -/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva -/plugins/cost-insights @backstage/reviewers @backstage/silver-lining -/plugins/cost-insights-* @backstage/reviewers @backstage/silver-lining -/plugins/events-backend @backstage/reviewers @pjungermann -/plugins/events-backend-module-aws-sqs @backstage/reviewers @pjungermann -/plugins/events-backend-module-azure @backstage/reviewers @pjungermann -/plugins/events-backend-module-bitbucket-cloud @backstage/reviewers @pjungermann -/plugins/events-backend-module-gerrit @backstage/reviewers @pjungermann -/plugins/events-backend-module-github @backstage/reviewers @pjungermann -/plugins/events-backend-module-gitlab @backstage/reviewers @pjungermann -/plugins/events-backend-test-utils @backstage/reviewers @pjungermann -/plugins/events-node @backstage/reviewers @pjungermann -/plugins/explore @backstage/reviewers @backstage/sda-se-reviewers -/plugins/explore-react @backstage/reviewers @backstage/sda-se-reviewers -/plugins/fossa @backstage/reviewers @backstage/sda-se-reviewers -/plugins/gcalendar @backstage/reviewers @szubster @ptychu @kielosz @alexrybch -/plugins/git-release-manager @backstage/reviewers @erikengervall -/plugins/home @backstage/reviewers @backstage/techdocs-core -/plugins/ilert @backstage/reviewers @yacut -/plugins/jenkins @backstage/reviewers @timja -/plugins/jenkins-backend @backstage/reviewers @timja -/plugins/kafka @backstage/reviewers @nirga @andrewthauer -/plugins/kafka-backend @backstage/reviewers @nirga @andrewthauer -/plugins/kubernetes @backstage/reviewers @backstage/warpspeed -/plugins/kubernetes-* @backstage/reviewers @backstage/warpspeed -/plugins/newrelic-dashboard @backstage/reviewers @mufaddal7 -/plugins/playlist @backstage/reviewers @kuangp -/plugins/playlist-* @backstage/reviewers @kuangp -/plugins/rollbar @backstage/reviewers @andrewthauer -/plugins/rollbar-backend @backstage/reviewers @andrewthauer -/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski -/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka -/plugins/search @backstage/reviewers @backstage/techdocs-core -/plugins/search-* @backstage/reviewers @backstage/techdocs-core -/plugins/sonarqube @backstage/reviewers @backstage/sda-se-reviewers -/plugins/stack-overflow @backstage/reviewers @backstage/techdocs-core -/plugins/stack-overflow-backend @backstage/reviewers @backstage/techdocs-core -/plugins/techdocs @backstage/reviewers @backstage/techdocs-core -/plugins/techdocs-* @backstage/reviewers @backstage/techdocs-core -/plugins/user-settings-backend @backstage/reviewers @backstage/sda-se-reviewers -/tech-insights-backend @backstage/reviewers @xantier @iain-b -/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b -/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b +* @backstage/maintainers +yarn.lock @backstage/maintainers @backstage-service +*/yarn.lock @backstage/maintainers @backstage-service +/.changeset/cost-insights-* @backstage/maintainers @backstage/silver-lining +/.changeset @backstage/maintainers @backstage/techdocs-core +/cypress/src/integration/plugins/techdocs.spec.ts @backstage/maintainers @backstage/techdocs-core +/docs/assets/search @backstage/maintainers @backstage/techdocs-core +/docs/features/search @backstage/maintainers @backstage/techdocs-core +/docs/features/techdocs @backstage/maintainers @backstage/techdocs-core +/docs/plugins/integrating-search-into-plugins.md @backstage/maintainers @backstage/techdocs-core +/packages/techdocs-cli @backstage/maintainers @backstage/techdocs-core +/packages/techdocs-cli-embedded-app @backstage/maintainers @backstage/techdocs-core +/plugins/adr @backstage/maintainers @kuangp +/plugins/adr-* @backstage/maintainers @kuangp +/plugins/allure @backstage/maintainers @deepak-bhardwaj-ps +/plugins/apache-airflow @backstage/maintainers @cmpadden +/plugins/api-docs @backstage/maintainers @backstage/sda-se-reviewers +/plugins/azure-devops @backstage/maintainers @marleypowell @awanlin +/plugins/azure-devops-backend @backstage/maintainers @marleypowell @awanlin +/plugins/azure-devops-common @backstage/maintainers @marleypowell @awanlin +/plugins/bitbucket-cloud-common @backstage/maintainers @pjungermann +/plugins/bitrise @backstage/maintainers @backstage/sda-se-reviewers +/plugins/catalog @backstage/maintainers @backstage/catalog-core +/plugins/catalog-* @backstage/maintainers @backstage/catalog-core +/plugins/catalog-backend-module-aws @backstage/maintainers @backstage/catalog-core @pjungermann +/plugins/catalog-backend-module-bitbucket-cloud @backstage/maintainers @backstage/catalog-core @pjungermann +/plugins/catalog-backend-module-msgraph @backstage/maintainers @backstage/catalog-core @pjungermann +/plugins/catalog-graph @backstage/maintainers @backstage/catalog-core @backstage/sda-se-reviewers +/plugins/circleci @backstage/maintainers @adamdmharvey +/plugins/cloudbuild @backstage/maintainers @trivago/ebarrios +/plugins/code-coverage @backstage/maintainers @alde @nissayeva +/plugins/code-coverage-backend @backstage/maintainers @alde @nissayeva +/plugins/cost-insights @backstage/maintainers @backstage/silver-lining +/plugins/cost-insights-* @backstage/maintainers @backstage/silver-lining +/plugins/events-backend @backstage/maintainers @pjungermann +/plugins/events-backend-module-aws-sqs @backstage/maintainers @pjungermann +/plugins/events-backend-module-azure @backstage/maintainers @pjungermann +/plugins/events-backend-module-bitbucket-cloud @backstage/maintainers @pjungermann +/plugins/events-backend-module-gerrit @backstage/maintainers @pjungermann +/plugins/events-backend-module-github @backstage/maintainers @pjungermann +/plugins/events-backend-module-gitlab @backstage/maintainers @pjungermann +/plugins/events-backend-test-utils @backstage/maintainers @pjungermann +/plugins/events-node @backstage/maintainers @pjungermann +/plugins/explore @backstage/maintainers @backstage/sda-se-reviewers +/plugins/explore-react @backstage/maintainers @backstage/sda-se-reviewers +/plugins/fossa @backstage/maintainers @backstage/sda-se-reviewers +/plugins/gcalendar @backstage/maintainers @szubster @ptychu @kielosz @alexrybch +/plugins/git-release-manager @backstage/maintainers @erikengervall +/plugins/home @backstage/maintainers @backstage/techdocs-core +/plugins/ilert @backstage/maintainers @yacut +/plugins/jenkins @backstage/maintainers @timja +/plugins/jenkins-backend @backstage/maintainers @timja +/plugins/kafka @backstage/maintainers @nirga @andrewthauer +/plugins/kafka-backend @backstage/maintainers @nirga @andrewthauer +/plugins/kubernetes @backstage/maintainers @backstage/warpspeed +/plugins/kubernetes-* @backstage/maintainers @backstage/warpspeed +/plugins/newrelic-dashboard @backstage/maintainers @mufaddal7 +/plugins/playlist @backstage/maintainers @kuangp +/plugins/playlist-* @backstage/maintainers @kuangp +/plugins/rollbar @backstage/maintainers @andrewthauer +/plugins/rollbar-backend @backstage/maintainers @andrewthauer +/plugins/scaffolder-backend-module-rails @backstage/maintainers @angeliski +/plugins/scaffolder-backend-module-yeoman @backstage/maintainers @pawelmitka +/plugins/search @backstage/maintainers @backstage/techdocs-core +/plugins/search-* @backstage/maintainers @backstage/techdocs-core +/plugins/sonarqube @backstage/maintainers @backstage/sda-se-reviewers +/plugins/stack-overflow @backstage/maintainers @backstage/techdocs-core +/plugins/stack-overflow-backend @backstage/maintainers @backstage/techdocs-core +/plugins/techdocs @backstage/maintainers @backstage/techdocs-core +/plugins/techdocs-* @backstage/maintainers @backstage/techdocs-core +/plugins/user-settings-backend @backstage/maintainers @backstage/sda-se-reviewers +/tech-insights-backend @backstage/maintainers @xantier @iain-b +/tech-insights-backend-module-jsonfc @backstage/maintainers @xantier @iain-b +/tech-insights-tech-insights-common @backstage/maintainers @xantier @iain-b +/tech-insights-tech-insights-node @backstage/maintainers @xantier @iain-b From bb74ee421a629248995b1a78692ed8fb3d6b8a8f Mon Sep 17 00:00:00 2001 From: Callum Linington Date: Thu, 2 Feb 2023 15:19:13 +0000 Subject: [PATCH 025/130] docs(azure): update token usage to align to implementation Signed-off-by: Callum Linington --- docs/integrations/azure/locations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/azure/locations.md b/docs/integrations/azure/locations.md index 93a7264af9..a0d933d824 100644 --- a/docs/integrations/azure/locations.md +++ b/docs/integrations/azure/locations.md @@ -29,4 +29,4 @@ The configuration is a structure with two elements: - `host`: The DevOps host; only `dev.azure.com` is supported. - `token` (optional): A personal access token as expected by Azure DevOps. -> Note: The token should just be provided as the raw token generated by Azure DevOps using the format `:{raw_token}` \ No newline at end of file +> Note: The token should just be provided as the raw token generated by Azure DevOps using the format `raw_token` with no base64 encoding. Formatting and base64'ing is handled by dependent libraries handling the Azure DevOps API \ No newline at end of file From df8782378d9a00887713cee932f9f71d50c87c84 Mon Sep 17 00:00:00 2001 From: Callum Linington Date: Thu, 2 Feb 2023 15:44:32 +0000 Subject: [PATCH 026/130] chore: prettier Signed-off-by: Callum Linington --- docs/integrations/azure/locations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/azure/locations.md b/docs/integrations/azure/locations.md index a0d933d824..1fc476f418 100644 --- a/docs/integrations/azure/locations.md +++ b/docs/integrations/azure/locations.md @@ -29,4 +29,4 @@ The configuration is a structure with two elements: - `host`: The DevOps host; only `dev.azure.com` is supported. - `token` (optional): A personal access token as expected by Azure DevOps. -> Note: The token should just be provided as the raw token generated by Azure DevOps using the format `raw_token` with no base64 encoding. Formatting and base64'ing is handled by dependent libraries handling the Azure DevOps API \ No newline at end of file +> Note: The token should just be provided as the raw token generated by Azure DevOps using the format `raw_token` with no base64 encoding. Formatting and base64'ing is handled by dependent libraries handling the Azure DevOps API From 654228f15a7e0f0e9fbb34788363812b5d87461a Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:07:23 +0100 Subject: [PATCH 027/130] chore: updating codeowners Signed-off-by: blam --- .github/CODEOWNERS | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7545302d93..ba00be1242 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,14 +8,14 @@ yarn.lock @backstage/maintainers @backstage-service */yarn.lock @backstage/maintainers @backstage-service /.changeset/cost-insights-* @backstage/maintainers @backstage/silver-lining -/.changeset @backstage/maintainers @backstage/techdocs-core -/cypress/src/integration/plugins/techdocs.spec.ts @backstage/maintainers @backstage/techdocs-core -/docs/assets/search @backstage/maintainers @backstage/techdocs-core -/docs/features/search @backstage/maintainers @backstage/techdocs-core -/docs/features/techdocs @backstage/maintainers @backstage/techdocs-core -/docs/plugins/integrating-search-into-plugins.md @backstage/maintainers @backstage/techdocs-core -/packages/techdocs-cli @backstage/maintainers @backstage/techdocs-core -/packages/techdocs-cli-embedded-app @backstage/maintainers @backstage/techdocs-core +/.changeset @backstage/owners +/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core +/docs/assets/search @backstage/techdocs-core +/docs/features/search @backstage/techdocs-core +/docs/features/techdocs @backstage/techdocs-core +/docs/plugins/integrating-search-into-plugins.md @backstage/techdocs-core +/packages/techdocs-cli @backstage/techdocs-core +/packages/techdocs-cli-embedded-app @backstage/techdocs-core /plugins/adr @backstage/maintainers @kuangp /plugins/adr-* @backstage/maintainers @kuangp /plugins/allure @backstage/maintainers @deepak-bhardwaj-ps @@ -52,7 +52,7 @@ yarn.lock @backstage/maintainers @back /plugins/fossa @backstage/maintainers @backstage/sda-se-reviewers /plugins/gcalendar @backstage/maintainers @szubster @ptychu @kielosz @alexrybch /plugins/git-release-manager @backstage/maintainers @erikengervall -/plugins/home @backstage/maintainers @backstage/techdocs-core +/plugins/home @backstage/techdocs-core /plugins/ilert @backstage/maintainers @yacut /plugins/jenkins @backstage/maintainers @timja /plugins/jenkins-backend @backstage/maintainers @timja @@ -67,13 +67,13 @@ yarn.lock @backstage/maintainers @back /plugins/rollbar-backend @backstage/maintainers @andrewthauer /plugins/scaffolder-backend-module-rails @backstage/maintainers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/maintainers @pawelmitka -/plugins/search @backstage/maintainers @backstage/techdocs-core -/plugins/search-* @backstage/maintainers @backstage/techdocs-core +/plugins/search @backstage/techdocs-core +/plugins/search-* @backstage/techdocs-core /plugins/sonarqube @backstage/maintainers @backstage/sda-se-reviewers -/plugins/stack-overflow @backstage/maintainers @backstage/techdocs-core -/plugins/stack-overflow-backend @backstage/maintainers @backstage/techdocs-core -/plugins/techdocs @backstage/maintainers @backstage/techdocs-core -/plugins/techdocs-* @backstage/maintainers @backstage/techdocs-core +/plugins/stack-overflow @backstage/techdocs-core +/plugins/stack-overflow-backend @backstage/techdocs-core +/plugins/techdocs @backstage/techdocs-core +/plugins/techdocs-* @backstage/techdocs-core /plugins/user-settings-backend @backstage/maintainers @backstage/sda-se-reviewers /tech-insights-backend @backstage/maintainers @xantier @iain-b /tech-insights-backend-module-jsonfc @backstage/maintainers @xantier @iain-b From 802513dd9ffa339ff3b679e2177a3ca6f9642f31 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:32:06 +0100 Subject: [PATCH 028/130] chore: remove extra codeowners Signed-off-by: blam --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ba00be1242..a89028a324 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,7 +7,6 @@ * @backstage/maintainers yarn.lock @backstage/maintainers @backstage-service */yarn.lock @backstage/maintainers @backstage-service -/.changeset/cost-insights-* @backstage/maintainers @backstage/silver-lining /.changeset @backstage/owners /cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core /docs/assets/search @backstage/techdocs-core From ac57d09f8fbcefa27c8caa409e1339eacfab5c34 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:32:53 +0100 Subject: [PATCH 029/130] fix cost-insights too Signed-off-by: blam --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a89028a324..5a51bd8b95 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,8 +35,8 @@ yarn.lock @backstage/maintainers @back /plugins/cloudbuild @backstage/maintainers @trivago/ebarrios /plugins/code-coverage @backstage/maintainers @alde @nissayeva /plugins/code-coverage-backend @backstage/maintainers @alde @nissayeva -/plugins/cost-insights @backstage/maintainers @backstage/silver-lining -/plugins/cost-insights-* @backstage/maintainers @backstage/silver-lining +/plugins/cost-insights @backstage/silver-lining +/plugins/cost-insights-* @backstage/silver-lining /plugins/events-backend @backstage/maintainers @pjungermann /plugins/events-backend-module-aws-sqs @backstage/maintainers @pjungermann /plugins/events-backend-module-azure @backstage/maintainers @pjungermann From e32d73b26a04e0b3243787c7eeed87c720aa9b7d Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:36:05 +0100 Subject: [PATCH 030/130] chore: fix Signed-off-by: blam --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5a51bd8b95..a89028a324 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,8 +35,8 @@ yarn.lock @backstage/maintainers @back /plugins/cloudbuild @backstage/maintainers @trivago/ebarrios /plugins/code-coverage @backstage/maintainers @alde @nissayeva /plugins/code-coverage-backend @backstage/maintainers @alde @nissayeva -/plugins/cost-insights @backstage/silver-lining -/plugins/cost-insights-* @backstage/silver-lining +/plugins/cost-insights @backstage/maintainers @backstage/silver-lining +/plugins/cost-insights-* @backstage/maintainers @backstage/silver-lining /plugins/events-backend @backstage/maintainers @pjungermann /plugins/events-backend-module-aws-sqs @backstage/maintainers @pjungermann /plugins/events-backend-module-azure @backstage/maintainers @pjungermann From 3201f1de3943d11c21a9ed7b8634c299764ac794 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:41:33 +0100 Subject: [PATCH 031/130] chore: make changesets unowned Signed-off-by: blam --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a89028a324..3f9e390a85 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,7 +7,7 @@ * @backstage/maintainers yarn.lock @backstage/maintainers @backstage-service */yarn.lock @backstage/maintainers @backstage-service -/.changeset @backstage/owners +/.changeset /cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core /docs/assets/search @backstage/techdocs-core /docs/features/search @backstage/techdocs-core From 6760ed19766d1240d9f7b4d76750a0e91d8c6314 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 2 Feb 2023 17:43:43 +0100 Subject: [PATCH 032/130] chore: only the .mds Signed-off-by: blam --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f9e390a85..bb6f8da19b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,7 +7,7 @@ * @backstage/maintainers yarn.lock @backstage/maintainers @backstage-service */yarn.lock @backstage/maintainers @backstage-service -/.changeset +/.changeset/*.md /cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core /docs/assets/search @backstage/techdocs-core /docs/features/search @backstage/techdocs-core From 335ce2a7a6fd86dcc04a51f00589f7e99b370854 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 18:33:10 +0000 Subject: [PATCH 033/130] chore(deps): bump http-cache-semantics in /microsite-next Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite-next/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite-next/yarn.lock b/microsite-next/yarn.lock index 5f27b0f062..54797053cb 100644 --- a/microsite-next/yarn.lock +++ b/microsite-next/yarn.lock @@ -6051,9 +6051,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 languageName: node linkType: hard From 9265d3fd7da3cfb66120e91043c39e364e860d76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 18:33:25 +0000 Subject: [PATCH 034/130] chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 in /storybook Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] --- storybook/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 7c73f5d0cc..8e5b2b8c56 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -6783,9 +6783,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 languageName: node linkType: hard From e446c345bab799356466f7e3695f5a8094a7c9ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 18:41:13 +0000 Subject: [PATCH 035/130] chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index cdf50e0c78..752aaf629f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25063,9 +25063,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 languageName: node linkType: hard From 5ecab049d4e8d39c95425cbef90bd2d48651f234 Mon Sep 17 00:00:00 2001 From: Mark Shields <4237425+beejiujitsu@users.noreply.github.com> Date: Thu, 2 Feb 2023 11:03:50 -0500 Subject: [PATCH 036/130] feat(docs): IAP, where to find audience value Signed-off-by: Mark Shields <4237425+beejiujitsu@users.noreply.github.com> --- docs/auth/google/gcp-iap-auth.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/auth/google/gcp-iap-auth.md b/docs/auth/google/gcp-iap-auth.md index f9a78bbc71..ce08f87865 100644 --- a/docs/auth/google/gcp-iap-auth.md +++ b/docs/auth/google/gcp-iap-auth.md @@ -29,7 +29,10 @@ auth: jwtHeader: x-custom-header # Optional: Only if you are using a custom header for the IAP JWT ``` -You can find the project number and service ID in the Google Cloud Console. +The full `audience` value can be obtained by visiting your [Identity-Aware Proxy Google Cloud console](https://console.cloud.google.com/security/iap), selecting your project, finding your Backend Service to proxy, clicking the 3 vertical dots then "Get JWT Audience Code", and copying from the resulting popup, which will look similar to the following: + +Screenshot 2023-02-02 at 10 58 10 AM + This config section must be in place for the provider to load at all. Now let's add the provider itself. From 39e2fac63f10ea5101171aa1ff64d36717b27623 Mon Sep 17 00:00:00 2001 From: Mark Shields <4237425+beejiujitsu@users.noreply.github.com> Date: Thu, 2 Feb 2023 14:34:33 -0500 Subject: [PATCH 037/130] upload screeenshot as asset Signed-off-by: Mark Shields <4237425+beejiujitsu@users.noreply.github.com> --- .../auth/gcp-iap-jwt-audience-code-popup.png | Bin 0 -> 38032 bytes docs/auth/google/gcp-iap-auth.md | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 docs/assets/auth/gcp-iap-jwt-audience-code-popup.png diff --git a/docs/assets/auth/gcp-iap-jwt-audience-code-popup.png b/docs/assets/auth/gcp-iap-jwt-audience-code-popup.png new file mode 100644 index 0000000000000000000000000000000000000000..72b69b3b01f6f0230c14f12eda50fed00f2d6213 GIT binary patch literal 38032 zcmeFZbyStnwl};Hq!Ex5X#@%BZjg|aMv#)&z^1!FkQM=@L22nmy1S)II;26m1mRm- zkLR5G-usUCy<>de_t)J+9eY2|dgim%Tr+<2H`g#!`S~*}bP{w31cD_iBdH33AR0j+ z2&bsX;7W{|q8J2%=i{NS<)mupN@?d{YYMZ1QaZWYK`EhbFjEM`ZDu0rg~NBgy1>P< zx7v3awC`DU%(v>@c%dkKE6(34_DCRR_^j}O6771O`18Qc?XBRtfmhb|hS0q6Ifp8* zPQs7-4S8pmB##rNm6v$#nrg8%nA=&M=TneXjnOuxK6`!h(`U|0WO)?)ygL51@u2crzvHI= z-I>#J|B7gA3x7dMy{YDiFmpQ5-0MBASrdBioqU6c^G07?V7jDK`xgW`BXM*q7pmG_ ztHUO~XOv86+Y0l+n>d4E>wRXp|JipO95p_O{=NSaHFR~_1;h>>j4nUI@$ zpNV^1O@^Izrr>kZ%WeMeG9}Pxc|z0k`*Kh6zN^YH7oCt;6y~kJv?wY%Ti1b%s`VFD z%)H!5HgowgS6lO9jGyBgl_rAgh(0rd=jUfH(@(H^IAv(|F3 z?qr@CGnaYmbBUs4yA@d!dcM<&vBfWEecJ|2x8jp?OqMsMa;trwlYB4x3ep*fGC$Ir znG8I8)`h`fhhOnx(8`(J1NxlbKXdc#_e2v_n>e+uo>y~&7fz%vL~5^k=0pcS_Fm?= zAKyN5l+L^IA86e8x_i_&i(Ovz6B=d5`I$u>dq(U$xm>Nhv0ie{Q+X_Ipu=mAdU?NO zEajph=iB5)^R2+-^=`+amvwPgg%L&9)F;Z3OuBzW`W+ud(ZCQ4+2U&Ysi@rQApow|2Ve@6YlJxcX?#w-pd+EtE+)out zkJm%L{4g#raKD^BS`e9vL1gIfjEK{;o!CP+O>JD4^?s-7raW(Ahg?0&P_PRPwSO6O zw+*a=&R1aegX{Azfy>g0(My`m{gQ&pu*_k?u)c+ucDsVF(eV$HX4s7Kr5`*cdJ^fd zXXBg=m4CJOMdjSGuu+Ghcwj`HG^H~Y_dzPgr)MubwnH`u6JDP`KanylGp)1u`bOqk zl%9K8ser6nhMPr*cV~l{lY5OMH%rQk{Jhe{HFIL?buFU3mcDy1CN%VcfsatPm4`hj z1rM__^xT7!PTV!0z2{BP!>w@4D`tZD)g{q(aMK%bY?;yvGtEK=vQUwgdfJ~C6(fz{ zW!dp$W^P4WeL|;FAn7UjmXmpD7Si4I-MsXieOXn7Q0VuzimJ1geXhJ6d2 zr1fc35AED4PnCE4*CQ~RHmwDDd8kN2+eqVv5m8LlEV%aP+Dn#YUh(^^EvZS~JPqk3 z(83B>9feJ;>+1TEVw-B}ylX=+g`uAgy&t3OENf4<*iL+)(c7WrY_mL$-cqWMJWifx z`*!@X#aC560zZjQ1CR-N>0;dbS;{-38aXewtsNc>xs&#qGJm&z=10w?<06|=MjSJE zm&}y$N(kS#8L)@KH$5?xRliDZUk*oC z#Ue6jP)CX+@$)HToHk%=CEiQPOn!PtpakvAn`)mHJs?@vBA9zEn&hoz!bINh%&BxzH_s(aDx z7GOAxe7A9IV>)gEk*&gE)_22~{Q0LfB-AT6Aw}fJG|y&2KUSTc*z-S3mU{Blp@&os z(Kl^1LeJ@-9GaJs9u`j6SHEgmAf=;>_KMdE%_$PI^}uSKhzXUy*K1GMC@X{+l`a93 z9v6A0GxVN_z~MQyzxp)NVcm)x_hx*{gzGWNot6Hb*K5+rlo?v_@@xW2_@7PEph#j> zbW4LWu0hw3=?cxYI{_)d)Iziv9C2JkU(vP5hY4+7S{UIJ53lKk5FK%$ZE>nunOPkm zF3r6U##3+Onj`zJ$hf9{$9c4+y>$}{1J;f3DV@&xDMf2xl_PhL?lJi{p)FXqc)-)|S^jGKc=S5F~U zkN9-WOU*da*OGYLkccg1I#+dE)S4Fa*FrHu6qe-thL6&(TYK)YE{5d8whSK)I*B-a ze4{@SNEyWR+SYV5>}|9-a#RwkK`hb(^@EOXEpZ8D25JIv-Y6swEcKsh2DlDiqm@q{ z#eDSRYkB-}LdB{wU4{*@kf>80D}5U0(I&aN=uocOC}T4%c8zD&zV*z^sGCwb!K-hv z@=t5;`4Ysm-ZgaJv}DAOYM10cpp@W5L~}8T?ZN$~lZ}@d(INd@!e>@q(Ric}>7g?0 zzF1&l+>*x_;-Ou{4%-t08dKE7Oe7_2frBh1krAQEz81%Jdm|7-rQ@p!`EH1B{l0QoZ>r)&>wH@*nc3zq$be1HQnh+UA zU0^3~sh15t@Ovg8f=UzmnnFG>$o;_b@?CIjh;oBols`V9gm`4j9dQM+KRAPe~$)l|06@>mN0$JN&kn%!~QN`<1%ClJcuVjdL!q zJH1Ay*M2z5B^qK~bnIi5NukUGWnAUNqdcwWl-<+(M4j0P#)Quz`7xjoLEW3w zj-R*ng{mW#n?ze|vOpi{?Qtsl&)au)KR+G8Tw21NZm5d13zlhGw3@}r0S9YylwxL$B0id!p7NZT<1ln%EAe0vnlHVdVMpZ~6F*py z#}W7z;asx3-S5umop;kygahx(*`viY6pr)D z#od)n?uqYNoM6e^sKAp<*_rIh49=yYr=z8|T6j3BN=vDP_Ua_E=UZ3*{XNE6m1Vv` zy!3YjMre|prgBbvdlT;XVIq65r%FY&g8`)72&zTCw~Ul=&xcDa`B6u<4)gJQUYQ8x zpt`Yh7bVbybUYHaPKT6_K1>*Xch>oHJi@uXyYx(mj#=S$qw#uyp7S$5dB`NzT~3-l zk-?!Kq>bXQ(>&L6P5ryHU%aLBDN?UDN%ezC%@hM`mu`>)y zO!8I8Av51iCRzT2yAspf+!z)b-X9K-6>w(ah}zy~_NQq_yqFobpefd-F;g64Y4bNj zG(6q9Q|FE`i$F+Ou32L&Lnr^DgXW1JQr_xbR7`;}HC@IW&q6Ax`mc zxE12DC@7Q(#&7P{O3^GKNiA&1p6972rP0#lk5goFIaP_q%=$;pn5ay((^r&G%Fgbz zlt%mvCJ(lF{`LHn-IVn9CgxzPP~=_1*g@pv}hAgFyO*mnk_mF?x)9R^4%6%jY9VN zly7!OuY_caU+fk((g&<^AZER!;u56n)mN7xO;ag-H1wS4q*F&QVaD z{EWp|H&zXzu^w{Vdn?tCJmUJ`oI>)LMEcLX{o!(x`HC_trQ-;HpZ2;(KNRg+4K?0yZmeF zBN};EHKW%zSq3Fs&9Aqnn7-~C{5+LAzKZv09KDwH6*Yj}JZecT;d*_4Y3N0gDB8gW z)7RA4RTcbNgA4&5c zccf|ixXd!KOfFO1b+Ij3zw%r3{?!{!2`NM%HPqRocj5>SO(9l{%kd5^np(J;uh`C& zh-Nfw;oZkmZ%>(Jv(#r6-)w6OuuV$oAENyhsF>{vHymm+cJS z=TG)Y@yJhA9Q5k%9-d;8o~zA_or)@`irNMvsw2VPZ6dpUkGsiw+WWI}Gkf{2jbKd~ zMFdZ3@YF5e^_98^^2eb}>jjE)G5e+Bx$o$q(?Tz`?rFYe<_W>Pn(duX5F5pb`nG7A zc8j+(lrH?-u*T1H)da8SD4QFq5b_?zG~}q`YTYe@K>6;m3_DJ+F~x zyF+>1qNZ@7DcJY*`k{nCLRV(sb3#DN`NJe6lw~C({xWfZfBWS9L0G0;j5ug9TTY7c z-X7)3`*Zx}5=K)6+b}fnCO%p zk&?S+VWj94XKxLSy>--o1YLz5;r5zOnXO;yp@;|&NTN^s1jaS%;R9~?);1npnc zJ@egnwNF_yINu;v#vnwzLw!ifsqoRw-_3N4hi@P6nYOlO=ul|4Vjbln%@jf8B44~N zp_}RWfN4~Ric#5kryS9j@;qE6yl!>H951SkvOf2)FPcSP&V(;9+V6Tp6L%`4-?vf6 zQHlq=5n#-1lm6tAjF0g^Kagp($| z!ESn-nY%Bsx-UgsK24(Gcp(>`L^Mw#nTpEVI#o>LvejVe>bi14v1{fKf>JQQ#pp4} z$kP-0opb)o7Q(5$`*W(HsdC8vC&VQ0#n!{6B^+WVm9x5``m4IDGh4;_*P?eh9Xzw- zrsN@zdqXfljA|(=2pHR1vlyD#8bMjytnC1C3V{fTy4e{TTSA>Eji6>Q8)54G`etfM zn29j;3vNX=MLP+oIZVdG0jlQlT;15i(wN_bT2usG$V~tUu!cGrQo31N**FTg2~+=; zD*(R3FSAlp{vP6FDNL=Us7xtg>j0(XV&P(8W0rD*xo}X6pi>Gtn3xKvN=pAj1o$OP zZSLe`C&0?;>gvkk%E@BuV8+VM&(F`w#=*+L!3;()JG$FA8M-msIMTpH{E;CEbu@N> z**U>%Z7AU~4UKG_orI~W!F|fV^Ru>7RQ#uS8^?cW0qBF(&CrgOorR6n+M4y>pKx@N zasfjAF`@tR2}gCnFSDvb9c`T*jG7Zty$5m-Fuvae|rtZ-&F~{HNhy zcK@~TzpDBz8;nv^6p*wvc81SUR#KQ6zB~aFTVt4s!0)fd#yn6qel|{KBX$l>W-c}p z9%e(RF()$z4=*PNH#?Mv-_-cuM9JDXIvLs+L*b%;a26Pl!wKayH8qBsG8^;o8#8km z^7AnB8=9~&^KkHS8SPfVi@(Ff|7Y+rO?TTNyf;+B#SZQ!5%8Q!1(b z>yA3i8mi`G2-g}r4=*<-4-XF;KNmkYH$T_E25CYa9D#wtg|f4;aB}>hD z?JhURpGCm8S^y{rY?dM1RsPESeI0JZR?hIt8a6O6n?KY0eM{?K_rN1uT&#bl^823l ze|-r_u0`Kq&;|1Iv_wIfD1Q|GxAmPN+fc|NQ#%(+c)G zKu}Wtc69+m<3Cz)G<1QQ{GKO}_2-tcxuK026vV`TIN{&xu>Znhx!Jkc_;`5Pn0eVb zOn}iFnlSU3@R%^0aPb)%@o_jggQvrTEl(#-&7@T^^aeF zRRxcu0#-l`z&Dvqpr(e-R!-C+YQ_#wV1?$kPC~z5X9fHBm;V(a#QI;o>CY4Y7Q4Zt zf3AU?08$L=KXc3Muxp*8@9xq=$h?xlMhs9%wdAsVcmeW6w59D7!<_fm}95SMW$V!T< zyUpy*yK3QY-0&QDYQL4yz=ctxPfL}i-Iwym(z$Q^u1w0fZsbvymQiSlC?1|7d$+Od zyKZ7U`Q5V4AZnic3~K6J)KT#y8H4p-{<}S;7*9}{mVVV1H=3B`hzt$zExIRJU7hW{ zp+tp1EG;dkXJ*=FW=OKLvnli6QKG)@HCb7NP)7H5G&3bVoA=1C|M9l!g-`OtrC#mV zq`{IOyw2TPE*{zh);jYX&}!|GhP5e;5f`ReO^w++tA}f2X&GzzFQw<*)3%!8f9Zd7 zVVcp`n9a8!r>3UP*xj{yL`O$0B0?S-8oJ^bP)v!+#?CGdX0yDzi&?8XIG$l4TKeML zHY3Q%NA#r%#`72nYgudSrw=PEhOU=;-Vm9VK=qixTC)Fz|5} zV~Ni+?v7yN;AopDc#%Ha{Bj_ah;G#D4~`eXX9Q-)p8>=n|8c-S zMf@G`PZ59L@mIuu{~fr0Mf{!S|NT4u*J=Kpgydfle+T@hcl@Vm{++~M5&sy+u0Q5l zlLcLI9zMiybab4ao^F|(BqS#%XXE89IrE5)jy~K-eGPXDU{8VR=~P3x8^AY0LPI|& zSS~d1D<~?aS~S+y*2>7reo**QR_52#B!ZW`t?uB!addRlv5$<5Tv}D7naD~*gQ%^o zz2ZtnMm9Y=D|%XMV`HPAd1bGlfQclI)BG_pF1iPEL;Tlahe}#lpfu0vGIlS0n{CS#mxGdWk`EM2fIyMokSdBf)^!S4erM&7LCs$$vbySnpqMFNl4!Qn6Kv?$$PGt z#D!)y_zAc9d~cR0p4C7z(Y8B+EP>lH&g7+q1>=(^Px31pDSR#v416xLF1*_U(55Rb zUsO)w5)q+*Pj5x)>zfDr&9@?umi~T>4<9}_r??z%40Y#g6u8~T#r+^n0-fl{&81gJ zU_)s1Jnza=%VET7gQS&~;soC#YkO$h;CJ)u3V2dRpY`?SMye4GGNi1mjL&%+e~6yi zgT>9mBVVE3X-lUc=>R;DRc#JjG~vx;t3qxv=QJIs#l;dF`MVKAxPg+?7!MJY9~ zilC1lAG+*Lt896hnwm0>Y0(CTq!8)VyGR|cf2AE`(QnWyayBtBp=4uI=Ga?q4@SLr z&nI835ZWKNIi82d34+6RrL+e3$kY^3N#j|PoDM`yO|9X0P|(iVIndu9v9PGH_ag}2g~gvIZ6~M zDv~F_Nq1Bl=3^SzuFqjV$9Nb{`@&H+bakJZaOV} zu}o#AUrFpQ+uEKs{M;@;#lTRTRi0m1Aa~tg=GIO#ucDNcTs;Lbf9vHwZ&@5^LGoc*=pTjXFQH`2UN8xP#Y}6k5 zh-KyI=;%22?`MpZzoAqo4!OL#q7f9-$vj0uL6M)?$)9EY9fO@WOPX(@ylf{6AMn`B zf!!NO79?VAy2vn@VQs#C11$Scfr^^3`|DS%uCA^qSdykvgUcRCR8&;QzO=OT%3^0{ z=fUCOh`I1uwVA0YG7%Ax={;OrcAT-ehrBj2P3PMa7{C?sCIvv$kaHaXF3b1&Mra71 zI>W)q*;!*o3PutVyU%QY^u0G~|MBxr!$8t(@ag`dK_-<9i2Es*x0&I~lyAerXp^lW zS;8I@0d>52K+775gm`!~Xv08mpTDHW=;&za>sy~sg0A-t4-b2Kd;PMqXlJUd!&EY* z4SU{Gtw5Tu?eXe;MFzkarkjHq0blaYnb*SoF)ovwJ1 z3A#vb3??tHuLp_yBMWQzn3$RxfeSUxJA@e-869PTR@TM03qos^?IO#i^<0fTLGj}3tPaP1o%0U391swIv|Kr*U*Pl+5>RH`jo#baJMp}} zqeF5T1Q;R(1qDXEW?=oLuUhX=2@2NF%+f!3f*;3fz(Pz+tX>yhtXGd-Z8Lu#h?0&V zWw^Bdd9qdMvaq(ko^R!Ox;>%PxB&L1ppch~t7751l%ZjIsmG~BYg=1J-*GQ^%f-)~ zl0cwslB~e0;|7+|;%n|&^AYOw&z~i=%8UXjy#_c*F1~kQ03X<7=jGRdfq;w*YD3_NQKyp9(wcQCAt9)-)&@;pmw6G3eSLD} zO_8EET9nk(`558ZQyuz^9#kwW&#!$BSENkL%-YAtD_-u5fKQE_g5qL1_5%*dOyoUa z6vU!FLhFOc7@nS2B(}$gtKBtDTlb~INdgB31{gO)?Uo>esiKi%IZBxz;gDFvEs6Hy6Yg`MBU>F!g`3Tr>FOhnEg&uQ`5=`-s9&j-@ethoyM?wTh3H4 zc%1F_@)iOUqvT3@d$M+Wtf-{a{aH3zSyffl_N)xd8>CkPWJ8Bq(yFSe;fdI;Zqe%l z#HOns^(3}aLqpg!((m3Q8FD0IPEJlTV(rBOXJ&K#p&LH=dUu)~o~gXutmo^vJ3KRsv z(@6pzC%MxEPQWPbzxUvN|NfnliV7i;T==Px5j9YhU~I4g1w|_3ENrg%H3c4>Ld$A* zWG4)G=f(O*+xJmXZ>FcUkdTq71qDIeol}N2<34;C2Jl6CpYg?B_2SaT#$#c3XD7DT zH(u}<3F5HZ!4d*IN8sZJY>nlF!E&q|GajKn&1^{65nu)JmfdEKueYTJY#I(%Au_ussE!}zJWrbg@HWK2n81}7^!TeC(% zT-*=DR95Y-;bGE0=>M@m5d~(UN5=ew?JA4ViG(2hJr9P35tkQHOr)Z}Ps9 ziyIjm!vlemN$>fryZ#jH#RxaQ=b6}Pn05X-w3pMr< z93}Pj_217y1efs<1qo^9^m9&5!Q4JmQoJ?1)Uoc^o(9tf7Hg1;IiRbh6~b*f`RZ#D z_vS*Q;DXyK798^d2s&W_)AI6aFacR^md&s&KrS9^J!$!uFYLx$?{a2*0HP~(-q9@U z5K9J7)(8%new{CN!eT2CXDTg;o+k^?CRX|U@;KR^C}@Lzci`77dKpObEMjReSr8&O zi{K3)(8bNwKGTy2q2b}N3bS9#lwuqE8n2J~r%MeHfnu^Jjt@Wpjm`c-2g0{z(PiZa zB7i=lyeK$KP#3(<=ljCuoShE(S({~MNC*iCmce@&C%&a$fa0U=?2Oc1|-06Kc$)73q9H@EQ` z$wKFyN#IsS;SusmN=hJ4YYOgzMEK~q>10%%gklINOI=QIJT?j#CVG)hb=<%#_}5}h zfLB8J@jcThqH5 zWhT4tvx5w|(C8^2r&IdMPkIOY-o3c%ln)=Mwq!@YepTG_hxvEM3!brY3IMwqx4yY+=j7D!o=W1;s?$Vvz3V<$q4)J+_XLSk z#WkP81cUtp0Vn#x!a}a3YLL*Q_NAnxfUo=YdBI>XMl$MwPyA}D-bZI=aikLZ`s6?n z>g!?e-l>1P$w~EjS#$#5mhiD+s$ot2LW5>)4pqjw zthu|pw+;`Nsw4-mb#tpvJ%94}F$xijE>5$Kwsty*gi-DTU%%4Q>ZGTqN4bQ`Sy6z3 z9xM5|u`nAz{lJP?Ln0!&-V!h|?%jDnzn9Pk3LQ`>)&P?%`XTS+RMA(#yXf^rKl@{4 zWo2K*=g*H~VJO(-F;1dI1qB78>=Mv-;o-FY3NK=wujOoQZySw%mThoI71tLEKN5fT z?U#3)`vT5raXLK(>Rl9+0;h`Q)m55A9*6HTmEsQH7Vz$hE9VL1E7PkBi2N>ovBGVP zMO%P>zv_*m)hahZW6`S%0Dfk8{$swUr$<@vcC9}iUT9ozh~Bn(?l-=8?3Ykm%MTX0 z1NF%Bq`yCY!smK*X5D)Z0D;CdfsYh`Ms!srd7B7TR{IWOTMtj@6qCkx9BtMI5)(L} zeQHJR-Q6hi@hlVO`qpbwP$Y3NP#cm9x%q;1g8U}$uwc%?GWDhB_~(0ji+K+wHIO57E*7Ln2qZdDWh`H{QLjIy$q+ zsc`@wNA2I-+$ia-m3Rr{dFEQ9!ZwzcDBUj#L$@(sq!l&Lv-0yl0Ax`+hyWcn-wW_+ zU_VzfSdOo?v-FI|Go&z_wnpz78XB%tgb}gI6$h4+Id5)l8Ey`zTTD%4KEu{}`SPvC zeWhf9yW%(<4Gad%BcRp*9Mm;Ma!3dXe)7g)+Ue;jyT^&S)nXGF0|UdwkNW-bIOVUY zq7*qwsXp6#+#Rnn>aa^I#pe;NE=NCfy?4RlX6;+=t>1471G~9YP}0-}D2aT601c^+ z?!|i*mb;7(m7Y*jKOOZM92@)6wj@gzZ3sfz$~mwx*;xB3Y*05N8-@D zxE(W1v1HmyAzJf%t{^KbuC9*%DLI+jh9aBGYMO0xb2AMfXv~iFq0~r#@MZHd73d^@ zA-dB5^owkSl?eD5pXgv{y@_8=ryHYFd;Z^cu5kFSU) zqVPV!wVA7-^05V|@#f~r7V`ne=hpSws?i_rhQ`K-pkkKYpcNMvFa7eRz1DG~0|u&Y zbAXAmcFIdj#Xw}n#m7fVt4K1ov0-`R@2_!Z@#s?Kw}oSn3ws2BlqQ?~^&>BKs@IWh z_~ZsCylQpt1wK`=#C~HvQvq{omF$mWrd{k8Nt;@ea5qLZdT5~^Q$&Icx&5{O+EN&G z%muECiZ#F}M7d}IG)l~Cqgy$t()8@xwmQ>YIr+{$WFtVv9GsuWt3!+OxfEa>A-G1A zn##()zP^yTI%jzf>;l^RxGEA8OS2(~2?=Qc{Q}^>(mgd*-L=EX#ij0k;5Os(XidHY zWa@$mCVlVmdRN=ta}Z}IWM>nOu-9Z|WeZa-FpsDO1W5Ao@))suXHc7VIEE^n$u0Xo z&^%)U7~_x=kV_WKtf+6~;%_F3EVM<~-oAU+ae2H!JI008(HHR;Ii?EB7*3%ACg;PE zKR452+N304(W0WGX~$kxTAZq1ZES9$*6Ok(R@uzgAt$fZquwIC1Yr?;E0z_iw;Aqbgz!uwwMPYK>TcKx4&DV2EXa zmeQ@U6Th~xvMRJc`2OQZ#QN8d@^{KW5e27HJH!f`68P*hzI%Fk2{m1=e7k={{~msf+SjZT}>usS(?u6_$>@$h;dY%Hu;Cu4n$SJ_{MrY7AB znb#tvaF9qvytG!=CK@U8Z73&;^#pt0BZ8fcx|#y1_0_9aX(M&t9coPq%lhM4<>fj6 zNuGcD)EB@rKP_g@^LbZ|8%}+YBS0muS#%0#KEIfLjZzq!nCN6GX*e3cs&P9s>eaac zc>jr3X@o2aP!UI(oxj9E?-42HQ(YxQSCgM(dQ;VQbig(F9zx7VBv4GqH}zr$|&(;IUfRI(c(0+yiC9p^dHZ^>d@uEbZaSUwO%QX*UV`Fyv6-j_ns3RAfoXm`-%3Tzft6t?{ zBrS~$OmV^{7(|{{T#`_b^3WiUD7?MBm;JFtaEy$Nne?ha5a4q=NL$+7Ei8|S6!|H$ z$5HRH7c~Ib0<|@frn4DK#vM4V^*%m6$K|H5@X7L8yn&`RvM(EOc=YPky*=hc*RFRD zNOFvIbwBl0f!zApWdZaI(v?z07?01l#-0I+OFwH?yTPr@ z%s^^?>f_@R2s0d@uFi>o8)z(Z z1Y=VW{;muK?UuyC&kO;?vp7cZ#i5`uC~~HyqtmP@25@lF<0q)0WUCGVH-`cEQs?v$ zCj(LEZS~KeKQF-vj~swKR{+-y2d@WxOeJX&!S~E(tC&eiNr8@nfwOdUb*%t~SK7_3 zrcVGcGl!r~o1770(Jn^<)pvA5^5PdW>|)*8H=ugT1X?nmDkfW5U0vSaj|5vJyKpnf z3yL$q%7}wH@qJtz-E(YIRNsOErfj9u2(b5Q0D^&HOiEXm^bR6onftM6>`4j-^sU@u ztTlO=d*nj&5e!HWL6==IWn~<=>4C}z96h51O(a0|c2T^(Rw)5EaJ}uUibU2-OfR&$}3d$4=fKLc)a_DH`mDbKwNy%AL!pDyoKs`mS zv_^7NR8$s=*_J;a@H>1~R##VFYXVsyZ)OLu5QJdCB;KJ3RYyRm~uGfZ+CHExVf#V#|J2reWS5# zMK&(3ar0jwVzEclsUd~5`go7=C`DQ^A!3fLG9sLeqoD3kq|j!sU_YFh$c zbWI`y{mwPGt7VNv=L6ym4s`)E2>M*zwjK4GI&bbf*{;G?Q{`2^Bi{}pLi!l&9m+(N zwGOF}8xA0`wQB6Bzz!;JT>~7kIbB8rVrLkb`*O6B$jC)&Np`E2xjDVQ_o+r@V{oqt z2OmGbAt1UBj*tDp$HW)5hN$LkWM&rp#cXJ0y3ClE$4V>f%FNn29B}QFTwM5|%kg4M zH!UCK*mA1a5b%r^)l~8QBU$o9Pjsp<0ptscjU@oSe5k88?wm*j8W9(u`C|8Dj=!e1 zHZM6xHk!`P)wSE;^{+tSr)?b_NN_&8&kH2XJXiPPqGPRxq-11efGobaxM*o_M^yaC zQ)H))fq~I*)W-yrfvj1mrNVUHeB>7u7&=jCtUDU28f!W0r9@Oy@ah;o21?2@LRjCddefE`B|HM~Fm8bJ0aN=&z( z;@N*%x_;A+0KPu3sDE2LV3z7lcWGm8jtT%99q6io45b7mk?F(_bh;F5p-{FugtJLhy*+2Pow)3Wi|x%L6@Z) z&=de@em;El83b6zjX^0lH$Ko-fj2v)-_RNjDLXlFo&A^(10ZY(1ZOY_|Iko00q5;U zKFIe-LjVkA2Vm#-41OyIS4FYRg8I+bzdhZ*jpdI2>3{!kRqzsqxAJO9+x-}d(3 z=Jo$y{qNuQ`?rt(pN<>+UB~CUbqaUj#?r+A8a(Xm>~N0-%;|^J)JV`*z$YRCOc%VY z;%Ek~pNkLuYr_*0fx0ZgfKmnxi1eHs6ac;sjbFX;1$;qBYWd=t13#zlpZAR%gM5Q= z0WzW8N(UNf3<-VtH9R%-3?zk-3YZ%4g9m`OL)B!1s7AG0_@ zpuBuB&jkU;4#WZKPU3>yF*7r(JT@0ljDF2@RbLK^&5Ev5y1Z6Jo(BKR2o@&hD-ixb zs0LXf!~J-@w7mQch`|2B1`j(6{KFseu7K|M=0pKeLt`UTGA|&;;OQUo3e+l_qgnF) zLhJiYSNBv@R4!iM?%%d{b*(2N8-Yp#1)`8D@(X+i&FNOaHh>IqAEawMa4R@5QM(bV z15oxI6+83w)x;s7ABGKXbgy)VfktHD>CO}l3rq1JVj1Y^l7O6$R|zVuPnpnPp#Sf- zk?g9htW5cQ=i3)Ef6R`-TL(PsebKfhrTX^Iv4)vmX>X6@J37BEmpa4JRVd?PW5PH^ z^-v%+jqB5Ch-E8J1Kplfu@IgL;4r!A2K4L5Aw~`^oRvE9AVLBtBBkWi2?wl_+N?hr z2lgAxIA8=uhAkgOAtqI`s|XJLYQdkwmI=}|KtWvCzjM22OCwreI&(GS^B&8puF^c_ zJRTStX!C%A_h;?BAbB}CWi71>T~bRKnI6a6BZ+6vP?80l+d$R{0;D+`jwj({p|(HB zY;eQr6WawWhgO{v1EghSq)Z&y2%B7}<@)j%0kSt!Sulp;1nC(Zl$MnZ)ly6oQEQS_ zAw1dM;hjTahrYykGrSZbF}smV2dZLr%gHDB`1mcYtq7c^17d(ZUs_rkUK$K94Cv-N z{7L{3bM!QJ^O+`)9D0J&&(gL<@e~0K@p(1DBYiG>#uj=u!ZI#N!5(Sn8$NU!cc z{MkH25cdIjv;wLDX*)YM9;@jVv3J{X$;o705oBS2YkmdVSI$k1fNxs?$w#ZwBKE>g zj{@xt1V9*Bb}s+|AL`yP`uCJyEe!uz9cT}Sf|3jZch?cX7$bXPvwh)ty0KhzcyL3* z=Q-n>E8?(ej0WlHXOxsvr+YG6Fqsf?qKI=FaU`G1h7*P*%dD5<$pjHgXQwM`tcYdO3V>e&oRI=5 zcML)pM?zUeg%y7HXubwhN~RJ62giK~I7GICOcoGJ$Hul5FW#_}r~hTg20_o844){D zl8#Tqnv&3&MgLlmg$wI4^6hYkBN- zs{Ss7lIM}snyZvO+7Eph#>Q^`#B@ojrG-w(*vptKLji{eGbj0}tWzm#zt#Zg)ZBWL zvgxj&r8S+VuTUzf5Dt`Fs8f9ps8||6gx%fU;YLqaV5NA7$e7BBuR-JnCt|t)>Sg!3 zuzR9chYxDn($dlvz{@^5{&{)1+)eRX5^Op+>a?`7@&j>DM*lOP?}nJ=AnUEz(Ec%aY#%IzSHI~0!Sb2SNqMZc^q1^use|XI%hn@I6~%^ zaDWW&%EHdUlkK2a3rYYPC8glZOd4ct@^=7QKh65`;r)9Yz;Zw!@FN%rN%=epw+HRm z@WK#O-(>rZ=P7;rpb2mRib#0%3OJD(kZrCn4&7G6dB@3v0f`O>YWPV)h%Y!{1Q^YH zvVpz%dZOl=pG-Cj4WWRjhEEa^DJ$BOVm3pD!g zvKTbI19cZ@^R|Pj1Czv~Ft4)u_6GF6;fWKpd!#{&>Y(>=Dm)mI>|KE~Tz0418i(P| z0ObW?KDYw`t5Ze(-_~%o9>XX0zMa5zpE~aZvLM}Q`^2He9`#nSjm&$xB=xmJ!dmY1{t?Yjsid ztrFQBGcI1tn}j9xN(9Z;Rcc}#F7fISYrFNFosc7WF*94wO1}h+jdeD*;>LxMe8;a7 zQ&;^UE!u%|0U|ZHv7SMCjUL>HWkK{H!-8t~{?5yt?d=ud7w}^`XS?70379nDC$^N` zhtCcV{URdnX%_2-1_X=>z}sR`mp2!y*bRV!RJw2Qf(RULI5<7+1VPn)sRe;gK)~YU z{od*-BIIJpA6wlgl>>?nKm6ho-@jG^4nctc90V%N6;J|bRL#oD$lz3`J8cY-fsQB& zWW33Hw#xb;pf?|a6N6!6*S~(jO&^dRAV55aCeFy=K`uaE?raXmd_a9;mJ5d~p?Htm zrlwxZ#ZjW7qM?a{9*@t}9xu|}yXl}tUE$c;+AmckbMUEf5-#@+=@RXFW4^;}bv5MCn#uU!Hh0hedm2a<-r7`4q!LqtlOuIW zOWDVYaQ-VA*I5MDJ@M`)XQlrmh)OZ1`7`{8B2;Pn*61|~UB!0)0+>wbK*#3Vk9=Dy z$FRd@w8tNaQ&`9)wv(THUg&)~T}F(VvXif>QAnACS}$@WF<$$4FW{&g{ezbvUiI_N z){G+A@cp&)s~^jAL2Q-X@4;o`n-Z4@1W562ZCJZJV#mqk#FTiKKPhlK@z1tG$PJ5k z+nbdeMFUbpIv4nfR1$Qqzl(T^;JOYzroR5}-xq0nE4=$_H{<)K&@(0Jiq{-%N*zW= z+2T>;sQv5vBT(_0^79|ji{(4T!TiA@MRqpc#G}UKRFAo_yO>i?4j!!Oyks76PhPO`Z63uzb;-&=ya3%v#4Fkcv85}H~6Ej~;h=LJtVhZpe zufXA3qxK`SSQ~Ic*B|5}cxnIryUE4RpDTa>_f?>42R)@_P(Z+&!QdDLpZn1xIPpnL zOn>j$!ReF%jfhB7-yUEtD8Mp}U{KJg>O6LvD%MjXZ^OVi1ne*6lP8z}55eaLp8OCO z2ageXGmMXbfTk_R7@rme5uU0YK zKBbhX)S1!=bpa1bqc`wdw^m@=J8#wUvc4lC#C1P~!lm=H{rp3`w(^$1J!UlDFF9D* zH~Ugv_E4d&+Z@qd&;O^r_W+9O>%IjWQ4mEzMLgCzpNI!ZU#Klzk7u24a;}BH$KhKeiU}#BE<`pIl&61LfZ3nDfb6%*+9pT zcIyqRZ!-ED!MzH5w;l^m>#P$DY6ZI4@vP1)ryh(v?~Sv(N-f#9J1!LH8T0ldW}2R4 zp5=GbL;}awXGYc<9)k51>Oh-R1w-YBqfS@)aXKRAi?28qdG5NC{3LCLf-S?=GeQM+ zjF8VI8>Tb$a?!7MRob4PiMcs4^r>&(M#pK#nYBiQz;^w5AY3tWI|fCc){5oT-kp|; zH;5(|oVft8<8&sD^T_v?-o4R%;azk%Q4z(dZ!Q{?KV7xJk-Y2uxMf~;GlQdoTOIaNQrk z|4*&0XCMdjPfAJ}&k2kZ1RlboWOsvRUnCHcPeFNHYBO~mLOdX)&45BOKMZxC^ZelS z3J(Rk3*UeMsemqofd_T9-$Mg5y|jR=J@AF$F19=V<8GhFYMnvwKu~rQT7Wnxs1p2v zBe6FTMC0=UimSYbRhNOg+MFsA`pDRL^XtMaFf06kvR`U7b{W8aX$bB(s5*}w31A}w zT%a|-kJbm^!uWT!Q8OMw(EeeI>?Y8p@`G&;=}baLr3n(olP?3^ZEbi+)1;leq_ngJ zwj+B;r;u0!doo;iROE}DwJ8ci@13iVUHJloYwz$-+k+aHIQK1cZq?G_Vzx+V14IS5 z-!AqN2D^yce3UkKJ-!A*Q|9*b8tWu2$hG46 zr<|>X_&sz?oEyA9cd<4eJtJ|tW@I>j@MkzkTvg)~%ziDVkwUs#A#`Fm{fw19`a^Iw zwQ@l&2_> z>s;kePpwOdgeOG4Tn<{B-Nq)z7CB~-zwlpSrt)(C{7{=P?r3hb=8Xj|zN>=Obyv#8 zA_1Oi(o%aul%2D!d9Y1paz3ZAIlFzepC^lstIeAigZ7M_+TY4mD957-=z6TrO4=I{ zyo%%dbciqA*!R#dPKDuse`ED()GYI7rlwu2S282>r;leuzU_a1tIRUKYaPH8Z`dNZ z-dyP@ii5>{JEW!Oyj%JyzQH}%Ts?;H)%K&Qbb%j*V)qs8a&%UIt2$6e*qGjvx#RaX z^TK0?Ywz)NX4}Ha?K1Lclh(AO;W|WFPI2E@=9! zPga#wPC})g#C#!Aybl(lhP*Dc+3cWBp%iqI-Mpu-2b}~K8^dgP3*%t)81vI{#Va35 zg?4~A2aT5pt3@mmmQj(BH-v?2wFYK^R*3%q&7*S7KWgWD5KRb1`>Rg@13N9rxidh_ z=CmH?j+$X(W#w>L(fK^@08376(20W6*nj+aqM_$1&&K-Z&@-GyAn-kTl0Hik#ct|- z9eN9-g^u%*YPvu}asz}52PJmAmpbmO;Y5_R&5)-ahdmSeQhu2-+GwDYc+iLD6^y;V z^TLzs`Rv>G7M+JJQ%cDFqlVV=l{E`YzMd@SZ-&_{51h)-N?(04U_vA3Q{z})DyVzn zDwIogUsw0c=JY%r$$XSnYPAdF_0|CTKHHp^^@Bn=tR|BK#0i08C!XFKVq#_Xgq^Fb z7ZaY1oaB!Cj5g{Jt>Q;bpG4T2$)7ZgYondpyyZPP&$4uRTfAe=G`)NSsglP6o*L(b z9Glka5n3Nc;~7@;X0dZpm(^Bi`F<=GXF|2$i7kGl-JF@eJh5F);wk4utrMr^ z`X^CCQ`2R$e_)JKDSoJjKf27jTcqpoCESOCE-RmJ+om}5l3kEWWxlla`*&6`4#vfW z7dj@%Ty-Fp(Jpv+$<_+HK2!L+n+%0;RTG31POjuV?VAFE#6X30K>?uU>O!G)-ix^H zgBRQOpr894!bltRu{caSNor4y5227v8ZBxA{mJs$;{i>Kpupd*q=DL1<#_W`k@e}U z6omA*Z_h%aE-Nd0&&cQ|?D?%T+}0!N?|{Kmq_3l&0o1fl>A0!YH8r`bif0(9%uJw< zyTdgVDP9>-{S8u7BG`bXp~sj`KRFZ*4O!?O;-a9zq5j>zP4s3J!v{pSZMr zR_>HKPQC4}pF$i)yi1JgOUWnT`k}Ga@6r~UI&y1`YS<=tLEL3xiD3cS)IO}UM?E@cAQ?#7}5T6dGE{iC%;O4lX{b%U4i zxp_-CnWuX3mkNoh5?y9K32ql5IG*fT1sSNuJe1~)qrP4=P!SAupw>AsW*8T{1~|#* z9jDG$G+Lgk%@$j6xkT6)BwZRuUlHjA=u<7X8%_Q~(+^t7K!4hjUyxH&)S!4V1>Ds< z%lrcQd+tl8ib77~MGp^QcXp(p&dGhaJ_wvmJZe#|7f_xVLm~wr2R^8|5aL!XumgVY z?gl0&f4|$yVx18japUZ0y|++3%(c8{aqjC~w5gwrbGoQAJtN~;R0}m31-e><1-iG! zH?qBOKD^m{sHC7E2|R^39?t{5pCX2?5;oE}5Whn#F*38`GyNPK!;v?*~H7@E%yh|DRiAP6S8SOVR4*e+K zpUg89tktnB-jMR}7Ty=CQ+%c|H5+=!NBj~J5uzg^jjE&_amX~nI^=6|LmLtUc}3V- zFMoz{t@h+A;`Z0fd?H&N^*r_)N$Ij>z$lCP37%saU$ik~7(3sOf$B%HGFf#629qeM zX|bxpVP0Nt!HBTs`itV%$AA{yDSw$GE0umBDt}^NptzG$nz=^K5(r@KpPIlux&8zlXNjd=N zivQUO`hlAQfP&%}_gUU4v<4M-%;Fb~hHMv`_0H!mI7m|Z*@?MuGh@p(+b`uK_oIHk z0ngoi!)B8QcnuA&j_?J{_c_WeLZ)d~`)gk_#qr*K_U2(=<7#-NE0bJtZOU&sY0V0u z-Icik%gHRMx6FRlrX8at!=>+V(1P)_^judsKV^q4ehy30fAJ*$b^kzzBA4x=H`qptb@aKF8F1I`IZHulZKDEt|5g2sNQCQZsheLbzwL~>Lmx{XLx13ge}LK z2r z;Hs{@iwo(=o8Vw^C2ylf-vKYL!w&3pBk zpi2CYQ)f206mqq%>v5}2l;@=>+8auV=lRvptKGUAZg+Guv7v#BD_)rL7Spai8F8s&}2E`{crKdfR6~2o-qnFAt52)lqP3oorOVG7i!bdPiRLm zTbZCx(8Y!})wBHkeE!;d)|q;egTQ3qY(QiHUJumDtye&6lW)a$qIyZ~mV%J|N5?Ya zntfhK_Y$wX?`xa7d~(ezt8}F9uKh2*x=rc*Hqt-4^Jx|kHe7gEX6)XPAkHBe; z3&|J_F%fiBSAuwgV<%x+b#}?>iIx%+G|y8iPs>$sJsK>l zYCE*}u7mcCw4<#nO$`=QF87aL9Nk{BM>PfoNjD8k?NIBKv!SD^1!5$AmE&p?Fd5YirBI*%1-nsbz19!F)0adp8ih#t4V8%m zB-7ylGC|g_qoV^gjr=Ad@7ZngRmK_Xj>g&}fWWgcdx2Q~+HA>sqx&1&V#?@HY6Iif^NmQG7=||05@O~DM-|dH3B1M=?D{FVkh%8h0f7jfu7OERN!G&9G#^3tSm5 zJC8Zq(atZw%HV+pKH|A)_XR3N&?JKfSf}*qB_vV;Ip`5cr+%1$ju4T<;6(1CrW-IA zWFPUvCu)OA4nbr9s&DG($o_;6`&FlIjSCxant{sj9BSMUqU3w#@kxk01?bZtkG)ss z@dC0!Vo&ZkP&&_jzsg93-vT){l!2Q2`GUaOc@8~|7ZcEbAfk3cy>Y%bzCqB3B!krRA}h@cgeiowTOH?7e8(ka)sPNF^5&deM`J?S)t2q$5tWr= zZ@_VkeQ?u71VC{|W7eA(P*7l#jvFLuUCnbJWOlyLABPq`uowA@Mik~ZF4Blnf-Lyg zSjp`t-Eo>M6ISD2?om=KevWzmqT%=fVdCk4XTN8QWRI~p4P|-?uZOcKLs?)Ks}U1r zccH$?czRMG>4iSc{*cG`YlCSD2N9~Y>F%aCSg-K-R{i1<#OXG4Cf=9XYGsoqu4R2qEa~uP9LQ=pBiO}NLQf=`(ZWw1%dqY2e%^M zsFOzevb|M#Stl92{=IKmJ!W8eQ!9`kRS~P=L$GJL>mFGp{sJ?OwIZmnPao&=; z4-XEEhjKI{sAJXD)IQow38K==>^sX#RA6GEFGD~3lGiCOPUmlP26W$K05rBixoBi- zO9b?(U(3t1P*H+55qMV3&@Idv(#1z14K4)ehj7jTOju+W2cmx)WcDqWqX@1rXbA9I zYa+tO7y}Oi*b074)$oBZrVqGe0ICPmUk8n|L)6qC1?mUVp=}T+@Mvgg5ZjJ{fy<(z zqEE**7mwc#XvLw@P1M~zJer{W1)Q{2AQQo9vG3Z9W~<*W5;WYm@Mh7Lz;funVvWAQH{pm7AvG=C^^jA2klYh2l@ofdr|^Si~uavu8BlE8M#cRnoG zhG+~0E+t47K}LuVjA%k&ZXi5KAYucfqYX%pM%LE)nTx}HV0&N!xQ!c-k?kQRL_$CC zFz0hH2eXr{{csVmlc$))^9u0{Mql(rJLz#X^VODsWSa0x{Ob9vMk7WQ2|hi ze)q~JwiWq8d_fCA`rlx$FQBOpjc+&U&+!u&FmZ%D?CflGpz+9JS!+rHlQg?)d${TI zN^b`08TS=G&Gsi@%gadN303S<! z7cCF-72gMF!En-q{HQ;Li=3~}!{-}Rw!|TYL2{oqwhO9}dar!jr2xlkG5P$W`%=wg zZM+l&Aaq=Awp)F~ZA1O?xv8gPTD!2saq2DZ3O;EqiGuL~f%R6AaF5&P?MJAMu`65r z``uSdop=%0|8ZN4=r>o2)errsZ`bQOjx*SY-Dy6}ZhIkP5{u>uc;~F1);{;m-a{1i zAwBh4Et%#tL8MltCLmT7OMcuyftL+seQT&J{?*M>CNE81A?zBw9w*+3=lay~MIm zhn<#$wl6exfu-gP?Va~x7=x8l)6fiJb7T^u@ymq0QE;nr@>A7lSNO@=7|Z4 zdyD*Ph*DIqETtZhL$u~^qzcc|E%^X@KhSf*Ss^wy_8YJ|Kf$VP+0}%YA7PrQ6dwr9 zWKGIBAy}mV@Bl@(HC-WwnN9x>f|f#4ya^0H;66qAvhd*W`QrxC@W!T;-HG3@VrlvL z$$=^F3ov^Dcx(XKvl&ouNFLi^z&Jd%e+jBo1VY1G4r!Fb4g~lnu!Mp>oXkuov{$Ik z?9*Tris=F!`fW&#W_T6b0CVhEKjI@3Kje~il68C=Nv9i2v9nVzFFsgvrb!1-@Q&G6Vlt_qC8Q!Fw03K=V&2% zH)3b^i3Zn*taHgDg4V- zc{Dj&vq=_fp%%T6#pCm(OF0ymw}<#*ur47XG%I+Gbc62WJm%&sI8-VvhjI@3=FKqt~cE6xMAKRe!G;VPqUH2<+6N zn%2n9SmJH%AsoqUNXQYwIO-QRx{3OxtQ-ti_nP;MXm`x4Wx_L}RobA-v=Q(z-nL#Byn^LbRMvT>fX`!EG8P#T!Ig$#2*qPC z;Wiw#4+rrI6<~+F@%h@hA!JP*Z*N1|nFk!9Z42Tb61mygszCbzTJ51evB1Oh^mIX= zKb7F0&~x~7bUsf`MkWY$byhJkG3c&Ze@_0&K?v|-R(-tz6a_KO-^pE$;^N}CL2@N- z>*LJ{{P8Sso`HQSy4AR9&Y7Y+=>*cGP7Xq)MB#m3>N7&|!*4S+45w4VbTWN!tu9QF zOPZMGD)Trq-J;ha6)RDW=3XA@I@XTZ?p>exQA7PxVzctX5GhYbuf^KXt?#sgB3_RNa%#o^CvYm>f8MwXkGr>Lo^nUkH}4@+dIukQo=o`;Vwx2#Ol+S=N} z)>hlovqn%*@ax#v;_|~gWC4`8fByV=Trx&NOnf7#IT_UC0RaI_Q0hY$;ww-b@$0my z&l;mRema*rEv}1AW;BI%dSk0LtACbvRPP7xJ1C$1@Bnp(377)QlQtEFTW-SjGNJ9X z%^U|JyQ{$Nae`4(h$t>D1~TYE%~i$Tq;hY}&x<=|>rtCqZXZ(Ry${_DiMB_v?!7%$ zyoc0vy=732?-fR>b=ajb!6~5a{4$uw1%!`e$LV?^Pjno@?Fdt zXszVxY7e%$JNmrwCs~H=s+jwEB_#?Je735ZJNB9yT8jtiBa7o_%(>nvu6#mHn9+

7*awPeQ`v}f zyHpP(O=!he9GI9zGm1CQNlD*-+VAVWl7O5DdHK_0VJRu;th`zYsn+(h#xV+V=3|Q& zxU`?#t+64O0g-SJJF3I`~sJ zTX#enD1CQjk#nH@eY}f(g5x`C`pw0YE;wW9@AI1uCuZW${A-d?|NScX4~ylW)uhcR zCXf0iKj%`Tsi&6qK6^ zz3n7GdGXwRV%%pXmJng=?TY{a(?p|lii^3TVq$VrQY50HqH=O_)J#lh15TkHp}@+l zv!_SP#ig9CV`j$iJlUPEAfs{~RN^xS)j!$X(?0-3?ljXJKY-@z^$lSltvZA80q=G3 zl^;thD_;R%aR#9foLxt6uL_Wh;{=^^o_vYD`Lfl)V2h7X63Ex+2A{ff=g&hp1nUb1 zXBQXw2v*XUd0M%rPMw-_5}bDuyb8*P#>RUfnP;J+Yr+0ng=*sJ_3KL%F7=s4C~w#z zpF#-(XA%SHoraE17DO=xpU!JA1e)5~F-Q<+(<}c41Un7Te*)bq+jFf-1}=tj!wZ~5 zjHpCBB2QguDR>A?fr$toJy9Rjb4|_7-o4FDO=s=H=o0wtCE#6>MgRPk%r|T> zn4g~~IRgSKQn-_fh9Q?4^ZWDR?*;<&Ne z3JNbFXODXKE?b7iTf=qIJ_ux2qHOxr+wW;Yl8#o)Jbnbf(~23VDYiE-lfXB{|cGUa#imGRgQ)i&+1L^^@v$OLB%j)uS-a7Cc z`}>5jFhm7oKHnLIu646793%rFeL6n1+dZIwA6zv&-^`(5Xjph~aBwLB--%+Z=_5LMFwGdnxtDpv?_paGQK zXrQ8lew;mbC`a4CT?)3+*_3Fwx4XN$Eo^M+>AFhBtdJRf{5TEVD7c}LS4tAq2PzG6 zFT(0$SD;dU&z}sa0MJ|)ZHMq-_0eSTd9-dlQ ze+zl5&*m4$HkCy8HQ(;Syu2@A_xbRI;55I)^Yyxc*~aS&6ujgxL@`!kN3HBWMzUOM zRSSvY?fdZdUOmos{FHvm4VEK!Ou)qoSDEb6C8OGnHi|$aa`^lnl5$7~NP;loi_VHvha>F$CY`Ig+ll6HgOFMgQD zh%|uBf%|xYzvt1&B*Y42@h~$nse<7~qT3Gw2n#tNO#v&2JZL;+vU@KofMPZ#5~Q{V z2Och9ZQ2$K3&I;hKlR2ok=UP)At52s!rtJdN9{g$DXxqL&sT>C0Cy6j_fIV?)wQ&u zAwYN?Av0^zMwp(K1`a)P5bBZW0KX);bSWA5y5LVoWGMie(;|es2<$61-Qvf68xS7p z7X3Q*RP4bKm>bXR1&Pc z!6cQlTPae~zN0VtN1OMwVE&huEUW>m?6ZHboGBq8;gv7BYvtB5QTbxXA_}Z!fTmdn z%1pT_nd(IF5YS~Mg|BzrKMfMFU5J4rtk<=nr3@o-hngw)f~czjWyJXS_}9tFJQyF~ zy{oEWc@QP0iR391$Hk$Mk*?VdyXBj{qrdJCCh^Arnfelyz*MRETkxiE>e=wY*37)# ztLv{7Mxkb7ut_y!oeyk_xF-)|liPVyPn&@rL?2YHBuLtjy5`CX;b5GvVP7c(etxDp zpLD#K#CS^FY8$@@sGjODJ*Zdbz$N2gxT0R_COkR5`^g}Hv87)74k5^<%`y(oT-(#0 z>-Klez%+`Olr#gDSsko@xJ{YJZo>O~3vnFLcV*(^(+1LXI{E3MB2z%_#w+bppY$fO z$yGx;cixVl5lI2>oPXJxGGSArq*q}x1g4Y8Kt~VZlJECA5`dn94Cq3kB75)P;PAD- zKN)I{d$x5u(GXj!2)L0~Geby#jdIFm!n%6)jSvY1g#yGolNoRfVwI}~nzd<>&YT^; z6W}Y$;IXBxrxy=(3$7`cPl7uNY+@wHNi5KV%Ndy4peyZ?fe8?mn0P01P|oPA@hsd` zK?ay3XSo6G`pqbxX3sz_IYGN`&upp}8Q@-Ef|e>c8jxIqgWwiVtj39WmaD0&^MDKy z*xm{NCUM^<1CIS3?kuPI>Ba)Ux#mo-}9Ky;5vo|xilZ6^Ii5DGjr07?oQ z*k=-nY=_xY&6COv0G#dI#a5A(oh^q<=XR!8er>HVkm9vLwn#z=2POn>u|7B^xDVc9 zLzl|J7K_-V2H=QWF@h~Y2G|@iF*3@+_SOC|lZcS;D{ZK73UuYPkRs-Gl2;0laFEnE zbq9uaInJ^G?w~0fq@afD7%wpkg*v4a=wEPK@t%hKGU|`Xc&`e8H%MRw7UR(PICI4W z8*DL<1VE@yy!HGBFC_*F`MC`@Cs;wRfBpE3Pvi9fh-3#tg+A#BZC(xAodR~1J3#w@ z%j)1OXf^H!VJ8wAyuY6}45NeiV*$K2bd^JTLDkRetH>(aU4cTLfC3+uCRZHzlu~AVPO!UnJt?_hegN1p&f&<&2#c85jO!dLJG%<(PfOq?)C_ma!y8(;nsCLN`h!u? z(HTHOHT-q`Gjan#LLeUjy<))}b`ee}1x}wXQYeI!4sk4kLqZt-vBmN+DG9tZTs}?f z>FC4)h?fC%^8NuNT^68*>FnxK2jemD57T_`-~$-<6mvdT{qmS=X9;hKPv<1@=-?ua z)u>%!XsMyP`dg?@L7X%vH1X)e27cT$2Y5Q)(aj&mX=ef@I#jPPjZv|&Zo}>uOsf{+ ze6rTP`Ma)n)b^UWOI~w9l0t571XZg;I|wCVo3|A)^eVPRI+3xlS)d2&vM77AC-mO3 z=^in~bJ!k^7BI&PSqGurK;7a2M_-@k4lpI-HLiuomH=FYeV;5;%pjyUNc09yJCZEI zQo9%==~AN)U%c^3@C9QB)Tz{>%9>4$I@867SC3H68^ z@p6U7RwDjc<1i8$n!>$3w<)JVrFtL8FTk4=jCQ|*`I{5)`Dgk|A)Kjl69#uYj(`Mc za4~XlQ3H%VV7g#vq1Jf>&cKY2!U6bxj)+Jb(h(R{zz;Hk6n`@*<^1;4dQNU`RCsvu zU&ND5>GkW^ogE$hz!jpG6WjYlbk+#PaSBnJ`)sGuK>T#QKiHcjW+-+z*2_pIPUy!O z-z~4KyyjY23&joe9*U_we%%al3iznQS_~-=SOpGQ>`>j@6y05gjmR5D#QTtl+q-uk zlB_*yPZtwarVN!m;=1f#;11Bh*<`kLgWA!Dfw?; zFqrOGPD$uE3&BT*B5=TKhlPpC_W4LiSl7MRJ&z7zqNA6^XT7dpYzM?##5QKwKY}_rtljsRf_L~<$?(| z1XcBNSA|?l%>LpOqvZ)T8jdMi@P{}q_sf7h5L_ToUCO%_k>(uFo10hbTXzNCiri1W ztEhx0GK(EWhli=EBGP_k$top9r&NxTx3;!1 z_ANdpLeMDb;GfZ?nwm7)rkZ8v#02u25yT!sWK+p@$i^ZqQi|!XJI;TP-TkNI{D-&uKe_z>|FmIs z{2xYJ6}i*?3o-rgv9kX#$^VZ4wEuQ?|NWZ!_euVL|FplI`zAwx4CMRQ?P0}R@lmd& z`cY*G0Mmwq{?;Dn4ie>IdB8B(I9OKdx&1RxaAvti?8=>*A>nU-W^@cQcQ1z0F*RBY zlZjH@*&-LU0^AOc#gRpYueY;pshN|vy2Q8AAS#?4+^=;sH_e~UFaartP2Jua!OU{6 zA(`MK;=@%To`dzW+w@>3xcvop!$i0h3% z(w0xuo)LzY7FO!_*111y9im!tx^^H|sAb`zR(wk`h?;Yk(tNP@ZqgN8KaxmMVs0IVGqE$kP^MvjF_pe1=WQ z&QN?W<)`tmkv#Q;M8}?nok*FPV~nZAQ+tJol;%G>>qNIdFWKJu+eIKxDn5^%+i;@E z4hj8ev7xx|Vb(y5PuA%9R3>fW()QDd=ffpE6DG2uCq)ucnp6>VHA8uZVi6w*KFu!Y zF#U59Au2(1a&eTFRHg17a>cPq@~<|rb$9X?g_mnFhsv+ZS1cDV(lRVA7PgtAjTc@%_cVW{XAiA< zQeS^8u&`(J+^o&q!jVXD8nbQw#iXmA)H%^6&krzS*q|S7mAn@qD=Jx@6wfw`-K-$= zp-#nC-z}uQO6Bq0mS?uO?O*qB-L@5fYDV(%``M(Y{EmXgdji*|D%Y0od9Oyx&MY_N z6%Ot;Y5nW+Hu4vg-dQf`OP)10YeL`u{7szII!?0JhfSO)??oR^hl^ZzQi zqs(2s4eR?~CpN2XQQYkMIJcy$Z(?l6@6F+EhDVi(;UN!aXAYBnxp@1k`;m^9e?z^i zS?T)Dfk?Lpb{6lS6Ya2ySZgr%UEg6@A6aTnP&#Tmv@?5au-s31} z7?#H0Da%d0wy+ZE%b77t?4!|2>T5sj_5Y~SeeP?6*n5tYdH*xI+bwlP^&@AbRy`Jv z#)}LMUH;yAxb}FlJ2WTnvuI{0aCwR3!p^z!VjymO_F4}<=$5mf5wOYnip|2x^=|K$$b z|3?G(|LxO$Rz%t|X~U<94DU0AmQ|OoT9EEDLm3aoFrTAW-MPp}NPdB{wgwW~QFn^- zR*pR8^Q|{6t*pL+@j~9upHBdN=rk^RK!rS^@)@*ld9BCt$xbYDU1;*clR6$luO=0s z4^1_-6lf(|{%8q?N--0#`$rD~jlfZ+gX5LKQLV?tyM*g|ne9STy+VBx4GUiM9jlyP zv+oqAX0LN4teJhbidOWFUghjz9WmBw!2Wcgh)^t!9kQ(w+3`BLQ*kp?CZZ#(p_Myr z+^K;qyCnCps@t%BXGy>SLbo8AHKHNi^PNcM*4Ewq15bBhHJO<9k=`dfT~>m3G0hxK zIfG&C@Y%fYNFMQAs7@3a8CnfjcJ}ZpJO44x^8(*H#ZB_5+1RfllUBSP^PF=I&b?i| zxAqSc-mQJ{SaPEJ!i62Z1Lx4pA(ydcG>u(I7&eim?p;Ldd@(D3*FhJPThlPMGL@KN za2QtOsri^Q9w3WJv-yd=CxiFO9)x214v)5{_zrB;KNMD13j%DC19t0x)}XnSn3*d+ z7cR)c51=H9wZ-3cnWAx1DQv%Mm}eG*&+^Dx* z^8=dN`suj^)j1;{!5Iwo80Ak$r<)nm!d}x0It3wHx z95i+YAcq7^Qxt<@><0u+H~}4Rc1DIYz^ZY4w$sm+OYCP6J^%{<)6jLON7pE06wvv@ zK7$B64!WU;v{$bP0T$ZN(1Ugj9ny^pdHtG7O@Aa`FB1S=qxC$1A45VzX`J?evxqbj z-gQ5ja_C}fPCT*8D9p<0gQAg6Zp6SdABY7WLsfNk`fzOMlf*y?;m=4*EBSK}V+4&k zq=DDk)@A{3VWzJIs|{(g85+*yV(jcbGN>fx!AD)pc;TyQX=MP}@AH$A#Wo6CN;Ni* zf*J5QVT|wQOp0Bb_2?{{Z#-eD02cFQ_9 zD=W(=|77e-qKKY)hT`JLi3BD?TCgG6i=9%)MG?%QBhtV_Vb7ugkieO1mq0vzl(GEl zS05-#8Re?K2T}k&OD6}=8{h|nKp%^I|6ceTO#%pL4Z3m*3r7IQ^xlHDP4mG@;_}wq zR3g9^2VcxY?|xfI|M1}+VCw1;629=E7@qy*@PiQ2W(6C5z*)BslU=~#3Mg362;8?2 zj4aAv0_rwv98=X<^4mp zI?@(l4DH)9ycE0KxhwZGoBWPSja6S994i+Hj?q_4Jdh2O65n-9BG9la)pwVfm^0&D zbX>f7u9WC#B{}DzshHg}@n zyynMd2l4XEc8=}waY4C(0ZU(QrtzhTdW?ZKID+&IsZ++q8?W*jNl{KnZ~(c#8M5ecHpGWrHZibxL;0gz-&+=lGt>|J1CKju|}BTTH+eUWlvY}sA+dx zzni&W>1)f=TpI(DTW_;H-w#b(*VGzJ&n9r0s=P7Emw;XGNu*sJf7o|R?4_>ZVY}>@ kb+o}ceV@&6sXyun<3AH7oKHMFhXi>!DdqbG5|4cU2YfoP)&Kwi literal 0 HcmV?d00001 diff --git a/docs/auth/google/gcp-iap-auth.md b/docs/auth/google/gcp-iap-auth.md index ce08f87865..36eece85c5 100644 --- a/docs/auth/google/gcp-iap-auth.md +++ b/docs/auth/google/gcp-iap-auth.md @@ -31,8 +31,7 @@ auth: The full `audience` value can be obtained by visiting your [Identity-Aware Proxy Google Cloud console](https://console.cloud.google.com/security/iap), selecting your project, finding your Backend Service to proxy, clicking the 3 vertical dots then "Get JWT Audience Code", and copying from the resulting popup, which will look similar to the following: -Screenshot 2023-02-02 at 10 58 10 AM - +![Identity-Aware Proxy JWT Audience Code popup](../../assets/auth/gcp-iap-jwt-audience-code-popup.png) This config section must be in place for the provider to load at all. Now let's add the provider itself. From d51668882d678bbaf971ea3d4f9e73c020e3491d Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:48:00 -0500 Subject: [PATCH 038/130] updates catalog-model changeset to include the tRPC API type Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .changeset/many-pans-unite.md | 5 +++++ .../components/GraphQlDefinitionWidget/GraphQlDefinition.tsx | 2 +- .../TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/many-pans-unite.md diff --git a/.changeset/many-pans-unite.md b/.changeset/many-pans-unite.md new file mode 100644 index 0000000000..59c068cec1 --- /dev/null +++ b/.changeset/many-pans-unite.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': minor +--- + +Adds the tRPC API type to the catalog. diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx index 3dbe941be3..d3fddf70cf 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx @@ -44,7 +44,7 @@ type Props = { definition: string; }; -export const GraphQlDefinition: React.FC = ({ definition }: Props) => { +export const GraphQlDefinition = ({ definition }: Props) => { const classes = useStyles(); const schema = buildSchema(definition); diff --git a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx index 81bec09769..af73335dbd 100644 --- a/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/TrpcDefinitionWidget/TrpcApiDefinitionWidget.tsx @@ -24,7 +24,7 @@ export type TrpcApiDefinitionWidgetProps = { }; /** @public */ -export const TrpcApiDefinitionWidget: React.FC = ( +export const TrpcApiDefinitionWidget = ( props: TrpcApiDefinitionWidgetProps, ) => { const { definition } = props; From f9ae9c3aa5dd746a313d1ab354ca7cec36e86d1e Mon Sep 17 00:00:00 2001 From: Amritpal Nagra Date: Thu, 2 Feb 2023 23:15:13 +0530 Subject: [PATCH 039/130] change hyper link text to be descriptive, rather than as text Signed-off-by: Amritpal Nagra --- docs/features/software-templates/writing-templates.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 71aa0ab758..6cab3947b1 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -112,11 +112,11 @@ steps which would be rendered as different steps in the scaffolder plugin frontend. Each `Step` is `JSONSchema` with some extra goodies for styling what it might -look like in the frontend. For these steps we rely very heavily on this library: -https://github.com/rjsf-team/react-jsonschema-form. They have some great docs -too here: https://react-jsonschema-form.readthedocs.io/ and a playground where -you can play around with some examples here -https://rjsf-team.github.io/react-jsonschema-form. +look like in the frontend. For these steps we rely very heavily on this +[library](https://github.com/rjsf-team/react-jsonschema-form). They have some +[great docs](https://react-jsonschema-form.readthedocs.io/) and a +[playground](https://rjsf-team.github.io/react-jsonschema-form) where you can +play around with some examples. There's another option for that library called `uiSchema` which we've taken advantage of, and we've merged it with the existing `JSONSchema` that you From 855be5bf86b52ff4f7e35be12bf0d90a5e681053 Mon Sep 17 00:00:00 2001 From: Amritpal Nagra Date: Fri, 3 Feb 2023 14:34:58 +0530 Subject: [PATCH 040/130] update url to point to new docs Signed-off-by: Amritpal Nagra --- docs/features/software-templates/writing-templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 6cab3947b1..a272f05f3f 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -114,7 +114,7 @@ frontend. Each `Step` is `JSONSchema` with some extra goodies for styling what it might look like in the frontend. For these steps we rely very heavily on this [library](https://github.com/rjsf-team/react-jsonschema-form). They have some -[great docs](https://react-jsonschema-form.readthedocs.io/) and a +[great docs](https://rjsf-team.github.io/react-jsonschema-form/docs/) and a [playground](https://rjsf-team.github.io/react-jsonschema-form) where you can play around with some examples. From 4b8b2faf8623757963e3f55ba17cdf293b3c90ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 3 Feb 2023 10:11:21 +0100 Subject: [PATCH 041/130] fix api reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/api-docs/api-report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index 070e950202..a4f61872f2 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -175,7 +175,9 @@ export const ProvidingComponentsCard: (props: { }) => JSX.Element; // @public (undocumented) -export const TrpcApiDefinitionWidget: React_2.FC; +export const TrpcApiDefinitionWidget: ( + props: TrpcApiDefinitionWidgetProps, +) => JSX.Element; // @public (undocumented) export type TrpcApiDefinitionWidgetProps = { From 9cfcdb99586e15ab6b630f118675c8c1ddbb6bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Wed, 21 Dec 2022 14:43:54 +0100 Subject: [PATCH 042/130] create a new branch with every thing from PR #14868 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- cypress/support/commands.ts | 52 + cypress/support/e2e.ts | 35 + packages/techdocs-cli/cypress.config.js | 30 + packages/techdocs-cli/cypress.json | 17 - ...eSnapshot - Backstage TechDocs Page #0.png | Bin ... toMatchImageSnapshot - MkDocs Page #0.png | Bin .../backstage_serve.cy.js} | 4 +- .../mkdocs_serve.cy.js} | 4 +- .../techdocs-cli/cypress/plugins/index.js | 41 - .../cypress/support/{index.js => e2e.js} | 2 +- packages/techdocs-cli/package.json | 2 +- yarn.lock | 1221 ++--------------- 12 files changed, 260 insertions(+), 1148 deletions(-) create mode 100644 cypress/support/commands.ts create mode 100644 cypress/support/e2e.ts create mode 100644 packages/techdocs-cli/cypress.config.js delete mode 100644 packages/techdocs-cli/cypress.json rename packages/techdocs-cli/cypress/{integration => e2e}/__image_snapshots__/TechDocs Live Preview - Backstage Serve toMatchImageSnapshot - Backstage TechDocs Page #0.png (100%) rename packages/techdocs-cli/cypress/{integration => e2e}/__image_snapshots__/TechDocs Live Preview - MkDocs Serve toMatchImageSnapshot - MkDocs Page #0.png (100%) rename packages/techdocs-cli/cypress/{integration/backstage_serve.js => e2e/backstage_serve.cy.js} (94%) rename packages/techdocs-cli/cypress/{integration/mkdocs_serve.js => e2e/mkdocs_serve.cy.js} (94%) delete mode 100644 packages/techdocs-cli/cypress/plugins/index.js rename packages/techdocs-cli/cypress/support/{index.js => e2e.js} (94%) diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 0000000000..fd4db77d61 --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +// +// declare global { +// namespace Cypress { +// interface Chainable { +// login(email: string, password: string): Chainable +// drag(subject: string, options?: Partial): Chainable +// dismiss(subject: string, options?: Partial): Chainable +// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable +// } +// } +// } diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 0000000000..c71790471e --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/packages/techdocs-cli/cypress.config.js b/packages/techdocs-cli/cypress.config.js new file mode 100644 index 0000000000..c4b007634a --- /dev/null +++ b/packages/techdocs-cli/cypress.config.js @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineConfig } from 'cypress'; +import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins'; + +module.exports = defineConfig({ + e2e: { + setupNodeEvents(on, config) { + initPlugin(on, config); + }, + excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], + }, + + viewportWidth: 1920, + viewportHeight: 1080, + includeShadowDom: true, +}); diff --git a/packages/techdocs-cli/cypress.json b/packages/techdocs-cli/cypress.json deleted file mode 100644 index b6f3b96019..0000000000 --- a/packages/techdocs-cli/cypress.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "env": { - "mkDocsBaseUrl": "http://localhost:8000", - "backstageBaseUrl": "http://localhost:3000", - "cypress-plugin-snapshots": { - "autoCleanUp": false, - "imageConfig": { - "resizeDevicePixelRatio": true, - "threshold": 0.01 - } - } - }, - "viewportWidth": 1920, - "viewportHeight": 1080, - "includeShadowDom": true, - "ignoreTestFiles": ["**/__snapshots__/*", "**/__image_snapshots__/*"] -} diff --git a/packages/techdocs-cli/cypress/integration/__image_snapshots__/TechDocs Live Preview - Backstage Serve toMatchImageSnapshot - Backstage TechDocs Page #0.png b/packages/techdocs-cli/cypress/e2e/__image_snapshots__/TechDocs Live Preview - Backstage Serve toMatchImageSnapshot - Backstage TechDocs Page #0.png similarity index 100% rename from packages/techdocs-cli/cypress/integration/__image_snapshots__/TechDocs Live Preview - Backstage Serve toMatchImageSnapshot - Backstage TechDocs Page #0.png rename to packages/techdocs-cli/cypress/e2e/__image_snapshots__/TechDocs Live Preview - Backstage Serve toMatchImageSnapshot - Backstage TechDocs Page #0.png diff --git a/packages/techdocs-cli/cypress/integration/__image_snapshots__/TechDocs Live Preview - MkDocs Serve toMatchImageSnapshot - MkDocs Page #0.png b/packages/techdocs-cli/cypress/e2e/__image_snapshots__/TechDocs Live Preview - MkDocs Serve toMatchImageSnapshot - MkDocs Page #0.png similarity index 100% rename from packages/techdocs-cli/cypress/integration/__image_snapshots__/TechDocs Live Preview - MkDocs Serve toMatchImageSnapshot - MkDocs Page #0.png rename to packages/techdocs-cli/cypress/e2e/__image_snapshots__/TechDocs Live Preview - MkDocs Serve toMatchImageSnapshot - MkDocs Page #0.png diff --git a/packages/techdocs-cli/cypress/integration/backstage_serve.js b/packages/techdocs-cli/cypress/e2e/backstage_serve.cy.js similarity index 94% rename from packages/techdocs-cli/cypress/integration/backstage_serve.js rename to packages/techdocs-cli/cypress/e2e/backstage_serve.cy.js index 15fc03f8f4..0267c8bfe6 100644 --- a/packages/techdocs-cli/cypress/integration/backstage_serve.js +++ b/packages/techdocs-cli/cypress/e2e/backstage_serve.cy.js @@ -48,11 +48,11 @@ describe('TechDocs Live Preview - Backstage Serve', () => { cy.get('.md-footer').should('have.length', 1); }); - it('toMatchImageSnapshot - Backstage TechDocs Page', () => { + it('matchImage - Backstage TechDocs Page', () => { cy.visit( `${Cypress.env('backstageBaseUrl')}/docs/default/component/local`, ).then(() => { - cy.document().toMatchImageSnapshot(); + cy.document().matchImage(); }); }); }); diff --git a/packages/techdocs-cli/cypress/integration/mkdocs_serve.js b/packages/techdocs-cli/cypress/e2e/mkdocs_serve.cy.js similarity index 94% rename from packages/techdocs-cli/cypress/integration/mkdocs_serve.js rename to packages/techdocs-cli/cypress/e2e/mkdocs_serve.cy.js index 1ace7298ce..d9da6caeca 100644 --- a/packages/techdocs-cli/cypress/integration/mkdocs_serve.js +++ b/packages/techdocs-cli/cypress/e2e/mkdocs_serve.cy.js @@ -40,9 +40,9 @@ describe('TechDocs Live Preview - MkDocs Serve', () => { cy.get('.md-footer').should('have.length', 1); }); - it('toMatchImageSnapshot - MkDocs Page', () => { + it('matchImage - MkDocs Page', () => { cy.visit(Cypress.env('mkDocsBaseUrl')).then(() => { - cy.document().toMatchImageSnapshot(); + cy.document().matchImage(); }); }); }); diff --git a/packages/techdocs-cli/cypress/plugins/index.js b/packages/techdocs-cli/cypress/plugins/index.js deleted file mode 100644 index 92faabb6ad..0000000000 --- a/packages/techdocs-cli/cypress/plugins/index.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -const { initPlugin } = require('cypress-plugin-snapshots/plugin'); - -/** - * @type {Cypress.PluginConfig} - */ -// eslint-disable-next-line no-unused-vars -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - initPlugin(on, config); - return config; -}; diff --git a/packages/techdocs-cli/cypress/support/index.js b/packages/techdocs-cli/cypress/support/e2e.js similarity index 94% rename from packages/techdocs-cli/cypress/support/index.js rename to packages/techdocs-cli/cypress/support/e2e.js index a1a5b10ea4..f06a08e9a5 100644 --- a/packages/techdocs-cli/cypress/support/index.js +++ b/packages/techdocs-cli/cypress/support/e2e.js @@ -30,7 +30,7 @@ // Import commands.js using ES2015 syntax: import './commands'; -import 'cypress-plugin-snapshots/commands'; +import '@frsource/cypress-plugin-visual-regression-diff/commands'; // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 4e6bde27c2..5f2e7db65d 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -37,6 +37,7 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", + "@frsource/cypress-plugin-visual-regression-diff": "^3.2.8", "@types/commander": "^2.12.2", "@types/fs-extra": "^9.0.6", "@types/http-proxy": "^1.17.4", @@ -44,7 +45,6 @@ "@types/serve-handler": "^6.1.0", "@types/webpack-env": "^1.15.3", "cypress": "^10.0.0", - "cypress-plugin-snapshots": "^1.4.4", "find-process": "^1.4.5", "nodemon": "^2.0.2", "techdocs-cli-embedded-app": "link:../techdocs-cli-embedded-app", diff --git a/yarn.lock b/yarn.lock index 752aaf629f..7844d0fadb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3295,7 +3295,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.20.13 resolution: "@babel/runtime@npm:7.20.13" dependencies: @@ -9428,6 +9428,30 @@ __metadata: languageName: node linkType: hard +"@frsource/base64@npm:1.0.5": + version: 1.0.5 + resolution: "@frsource/base64@npm:1.0.5" + checksum: ba526dd81df4e91b0abbcc34fc6818c8ff132e5230c23a4448a365ba934f782a86abb2a8566050f9450905851e467a7160f01f3d321fbb0fa3c8c516c5a82b9b + languageName: node + linkType: hard + +"@frsource/cypress-plugin-visual-regression-diff@npm:^3.2.8": + version: 3.2.8 + resolution: "@frsource/cypress-plugin-visual-regression-diff@npm:3.2.8" + dependencies: + "@frsource/base64": 1.0.5 + glob: 8.0.3 + meta-png: 1.0.5 + move-file: 2.1.0 + pixelmatch: 5.3.0 + pngjs: 6.0.0 + sharp: 0.31.2 + peerDependencies: + cypress: ">=4.5.0" + checksum: 7e5a1daf3a36ec7f62ea643da8eff73aadb595e4fb8b400abec37e38a356a200de1ce47ff2e14666e71556300e4d044d5fa5ad21bdd2d214b93402addb1a490e + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -10794,460 +10818,6 @@ __metadata: languageName: node linkType: hard -"@jimp/bmp@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/bmp@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - bmp-js: ^0.1.0 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 3a5a7600add0edf68a0d8f8397dd0ca37fa20d93b4d9ea8321875acd92e4a632a31ac1af0fc2129cff76cdc77aa77b359327613f822c0bc48781f007459e44f8 - languageName: node - linkType: hard - -"@jimp/core@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/core@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - any-base: ^1.1.0 - buffer: ^5.2.0 - core-js: ^3.4.1 - exif-parser: ^0.1.12 - file-type: ^9.0.0 - load-bmfont: ^1.3.1 - mkdirp: ^0.5.1 - phin: ^2.9.1 - pixelmatch: ^4.0.2 - tinycolor2: ^1.4.1 - checksum: f67db147a114b7a220621b80f9c65cf0d1434f4bd7ab8203b44246b79093f9ec46170a6205755eae7fe53c14d2935d8565f5a2185ccd4495ae1cd6f13bcf4852 - languageName: node - linkType: hard - -"@jimp/custom@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/custom@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/core": ^0.10.3 - core-js: ^3.4.1 - checksum: 8a282c9387cb975a533d140a8e0c9f7c23b12b6e6a92e9a1e979b7cc7d89e623189c47b0d0119aa7e497149f0d9a85e4ec14a605921527340c307b7750ba519a - languageName: node - linkType: hard - -"@jimp/gif@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/gif@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - omggif: ^1.0.9 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: a23253e6762e6f19b0fa6deaa9db3bca25b313eb5ea506dd3d844407598d86cf4188cc92c091019a4257450821ffe24f0475cc2e4a1ef24998d95d176078cf2f - languageName: node - linkType: hard - -"@jimp/jpeg@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/jpeg@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - jpeg-js: ^0.3.4 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: cb830ddf3fddbc4f833086ffbf8f21e0c73863522cce86d6f6c82d4f0183d0619ee280736c5ed07563bfcf9f884fd96bdb845a95c5a8a6e11f961f4e95cb4b24 - languageName: node - linkType: hard - -"@jimp/plugin-blit@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-blit@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 5858557359eb0405c29741c8ffe98b4bb26b42564c178402e21b74733c02e04233d2a1af095cff4cd2864ab43d79e01033559f250b3eb879600138007a7d7453 - languageName: node - linkType: hard - -"@jimp/plugin-blur@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-blur@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: d3a2e14d4dc5f3b5ad010888e70193cc8f7327aeda67989eba3f49de2e5324fa6adf89e3a7f1597343f59688ce6e299ad5818aa6c670ed8596ddfba0ca281db3 - languageName: node - linkType: hard - -"@jimp/plugin-circle@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-circle@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: d351c85f4a9aeb404302a03f5d99e6486b95f271f2c628758c8e3d1574bc268278ffc34d9d00d3221d6079431b06a5e6a5a3922863994000b5eb3c81f8c27d35 - languageName: node - linkType: hard - -"@jimp/plugin-color@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-color@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - tinycolor2: ^1.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 2014802f3b0465abbd7682336836183293a26315acbb84826f5207a374f807c574e120790738dcf51aba2cb7e9a481003289ef8bf429facb9fe36a19ce5b6924 - languageName: node - linkType: hard - -"@jimp/plugin-contain@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-contain@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-blit": ">=0.3.5" - "@jimp/plugin-resize": ">=0.3.5" - "@jimp/plugin-scale": ">=0.3.5" - checksum: 91d77621a350a733839fa8bc15880bc74ac666682a651e676797207dc4f55cba465f3695f6280f3d05122d67067a0a94f3905f323410d880484dd769055c9b49 - languageName: node - linkType: hard - -"@jimp/plugin-cover@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-cover@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-crop": ">=0.3.5" - "@jimp/plugin-resize": ">=0.3.5" - "@jimp/plugin-scale": ">=0.3.5" - checksum: ca664509c2d42e987a56e80fa848ec3e5590a1f919c0a607d7a03b8192571456c0287a2f768785bf816ecfe7889a7a786490a408866acc94ce5306b39ca82f6b - languageName: node - linkType: hard - -"@jimp/plugin-crop@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-crop@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 54e79b6fa831f813f28dac53187dfa1684d00ece8a98433c30c1e61333ba2b5373758889d202418a55c99dffe235daf153454f35691324b42dd9a103a65d3bd2 - languageName: node - linkType: hard - -"@jimp/plugin-displace@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-displace@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 17b5792ce07ac3b7b5aa8c457626fb46ded0a8f05027c4580fac1b22428e7c2c0ff04e0ad4f7249c179322afa7921e57afc88ace03e2d08b67e864c24ef5c060 - languageName: node - linkType: hard - -"@jimp/plugin-dither@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-dither@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 77caaceebccceb73e32616fd79517d9640d217403577715e08146bf2a67599822070be8a51783ad76402e0303f6bc9afcebf75f75cc73ae44c684e3c1e1248ea - languageName: node - linkType: hard - -"@jimp/plugin-fisheye@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-fisheye@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: f60d9c6c40d6fa1b85658ffa23568f95aeab1635165f2b74266b128be0f9b4bf963cfa755e5a43b7f18421340a4bb7b628ab75821b34b602924154b55c0f39ce - languageName: node - linkType: hard - -"@jimp/plugin-flip@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-flip@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-rotate": ">=0.3.5" - checksum: 6542b9e595e3b95cf8ac741226e9089ab1edd3fa19bc4ac159afd5bc3801a55f6ee93ade029626dbde7977eb1fbca51c71d9c6546b5285754eb29b54a7f5f5ff - languageName: node - linkType: hard - -"@jimp/plugin-gaussian@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-gaussian@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 465ca178b9a9062eb6db4a57fe3b47236270bad9600cfb4aae162aad43f20e5cf90c19bf77bd08867f25abd0679c6018412e0ad8cb3777640dad5c3f51e64dd9 - languageName: node - linkType: hard - -"@jimp/plugin-invert@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-invert@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 917419301a961541466e04c7fc974fc95f35e3f95f75e6bfef141270f06b68bcdf61321cad2d545cd8115a6e3a99992345f964bdb94cbdb48aaf42885b8fcae7 - languageName: node - linkType: hard - -"@jimp/plugin-mask@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-mask@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 7870fe46d9fcec6145476bcceed23568012e87953772e7ea2b82c8ed48375ee525db6b977816574b47e08c595bd6123c8d9cd24a95e649677d0ec1fada4e6831 - languageName: node - linkType: hard - -"@jimp/plugin-normalize@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-normalize@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: f34ebf3ef03d83b2cb9a8779d39a3e10707aeae4dfe67e1bb30202589e72d09c0e9a61b429de4e4886728f0faabbb83a6ff081f2e9bf79b517e2ea387c53a9a4 - languageName: node - linkType: hard - -"@jimp/plugin-print@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-print@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - load-bmfont: ^1.4.0 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-blit": ">=0.3.5" - checksum: 772a7d82741af583293006f699bbbaf116c264327412ea6ed0f3b7fb14d0dcbef51278e6fce3e966ad23e655114bfe949106d551d06459819b26e461638e8a5e - languageName: node - linkType: hard - -"@jimp/plugin-resize@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-resize@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: aff691d2936bd88d59a68b36a6f06b306d0f7c654baec7b43371e6b250397b3a19d0b98a620ad26738ed51357f5899a8002761d8f6f0ae6074e6c63905895a5d - languageName: node - linkType: hard - -"@jimp/plugin-rotate@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-rotate@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-blit": ">=0.3.5" - "@jimp/plugin-crop": ">=0.3.5" - "@jimp/plugin-resize": ">=0.3.5" - checksum: f1771365753a64387e455006467dd41e09c1e329180e57df77ef78f72dfac7d889e5d03adae1038f4c6ac081a9d5113c59541983fdda6bf08da9af852650c468 - languageName: node - linkType: hard - -"@jimp/plugin-scale@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-scale@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-resize": ">=0.3.5" - checksum: 356e8ab040bf842845a29cc0a6d7170fd15c1a9e9407028b88771bff01ecb6870c8f26d15b59b432914c430e611f36f54f932aaa7a68dcc9e32efae2f6dd1946 - languageName: node - linkType: hard - -"@jimp/plugin-shadow@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-shadow@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-blur": ">=0.3.5" - "@jimp/plugin-resize": ">=0.3.5" - checksum: 7f07387160b31732b3d43a2c4d44f8716be7a08b2f0fcfd58d18ca76ed62d825629c6273434bd23d02ade6a18bc8d7c7020eb9ab0ccc95acd85e718b5ab2c7a0 - languageName: node - linkType: hard - -"@jimp/plugin-threshold@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugin-threshold@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - "@jimp/plugin-color": ">=0.8.0" - "@jimp/plugin-resize": ">=0.8.0" - checksum: 550de37f82ccfaacb954b0e183a7bfa77f80f33ec9f5b08756ce778675284e486a2b5860c69c6d15a38d1d7030f1896e1ac79e8675603ead9e36c075f3cf636e - languageName: node - linkType: hard - -"@jimp/plugins@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/plugins@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/plugin-blit": ^0.10.3 - "@jimp/plugin-blur": ^0.10.3 - "@jimp/plugin-circle": ^0.10.3 - "@jimp/plugin-color": ^0.10.3 - "@jimp/plugin-contain": ^0.10.3 - "@jimp/plugin-cover": ^0.10.3 - "@jimp/plugin-crop": ^0.10.3 - "@jimp/plugin-displace": ^0.10.3 - "@jimp/plugin-dither": ^0.10.3 - "@jimp/plugin-fisheye": ^0.10.3 - "@jimp/plugin-flip": ^0.10.3 - "@jimp/plugin-gaussian": ^0.10.3 - "@jimp/plugin-invert": ^0.10.3 - "@jimp/plugin-mask": ^0.10.3 - "@jimp/plugin-normalize": ^0.10.3 - "@jimp/plugin-print": ^0.10.3 - "@jimp/plugin-resize": ^0.10.3 - "@jimp/plugin-rotate": ^0.10.3 - "@jimp/plugin-scale": ^0.10.3 - "@jimp/plugin-shadow": ^0.10.3 - "@jimp/plugin-threshold": ^0.10.3 - core-js: ^3.4.1 - timm: ^1.6.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 28833f4ce43f911c41b5c251759f5e38406ba99249a7c00d11a81e4ce58027eb993ca0c220601c3702832c2855dbb0554762bf724107bb4fdd0a2c1cf8753895 - languageName: node - linkType: hard - -"@jimp/png@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/png@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/utils": ^0.10.3 - core-js: ^3.4.1 - pngjs: ^3.3.3 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 1520b669d0dc6a0b47d7357c5e75a080e3a56db0459fad5c56e299b03c4a7bf560540480b08fa2e1eaf283ba153efb26a3e90d90fe373069c6c6184f7ea3e163 - languageName: node - linkType: hard - -"@jimp/tiff@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/tiff@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - core-js: ^3.4.1 - utif: ^2.0.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 6ff41bc962afdbcd223b331beed232b5aecf2b0ba06cccddaef5b5d4b925fa51d6a26d226080baabd25d7dceb0b9b0e5c69b60c44494822cb69988c3221c6514 - languageName: node - linkType: hard - -"@jimp/types@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/types@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/bmp": ^0.10.3 - "@jimp/gif": ^0.10.3 - "@jimp/jpeg": ^0.10.3 - "@jimp/png": ^0.10.3 - "@jimp/tiff": ^0.10.3 - core-js: ^3.4.1 - timm: ^1.6.1 - peerDependencies: - "@jimp/custom": ">=0.3.5" - checksum: 68ea0f1a1f3116ba779c898c9fb38fb7b822addaa14ac153aea4cdc9e274efc34a46a6ad4c8a9978fb88b4ab5a826190e28b89b431dce735ee760997f2d66678 - languageName: node - linkType: hard - -"@jimp/utils@npm:^0.10.3": - version: 0.10.3 - resolution: "@jimp/utils@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - core-js: ^3.4.1 - regenerator-runtime: ^0.13.3 - checksum: bd36b8bbaaf878190afe357f1bcebed9215dae503f06603039e6a106045588003b845150cb5a5ba6dd4d973e7e7b95779158ae921011fb6bd79083c8cb177804 - languageName: node - linkType: hard - "@josephg/resolvable@npm:^1.0.0": version: 1.0.1 resolution: "@josephg/resolvable@npm:1.0.1" @@ -13831,6 +13401,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-techdocs-node": "workspace:^" + "@frsource/cypress-plugin-visual-regression-diff": ^3.2.8 "@types/commander": ^2.12.2 "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 @@ -13840,7 +13411,6 @@ __metadata: "@types/webpack-env": ^1.15.3 commander: ^9.1.0 cypress: ^10.0.0 - cypress-plugin-snapshots: ^1.4.4 dockerode: ^3.3.1 find-process: ^1.4.5 fs-extra: ^10.0.1 @@ -16573,13 +16143,6 @@ __metadata: languageName: node linkType: hard -"after@npm:0.8.2": - version: 0.8.2 - resolution: "after@npm:0.8.2" - checksum: 52ea9be2e97d764de256dfb3843d68cb180d2d464748010ea0d8079ee28190190080a53e4005a62adbf4337c2d613906db82f08c0091cccb8e817625ccf94c52 - languageName: node - linkType: hard - "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -16776,13 +16339,6 @@ __metadata: languageName: node linkType: hard -"any-base@npm:^1.1.0": - version: 1.1.0 - resolution: "any-base@npm:1.1.0" - checksum: c1fd040de52e710e2de7d9ae4df52bac589f35622adb24686c98ce21c7b824859a8db9614bc119ed8614f42fd08918b2612e6a6c385480462b3100a1af59289d - languageName: node - linkType: hard - "any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" @@ -17160,13 +16716,6 @@ __metadata: languageName: node linkType: hard -"arraybuffer.slice@npm:~0.0.7": - version: 0.0.7 - resolution: "arraybuffer.slice@npm:0.0.7" - checksum: c6bacada71e8fe3e63c5f5ac5f23810e617faa1bcf33be21db43f40819edc7e186172d4a6fb8f1b9baea26689fc8da6ddeb78cefa2cc9723156c0247c3587118 - languageName: node - linkType: hard - "arrify@npm:^1.0.1": version: 1.0.1 resolution: "arrify@npm:1.0.1" @@ -17712,7 +17261,7 @@ __metadata: languageName: node linkType: hard -"backo2@npm:1.0.2, backo2@npm:^1.0.2": +"backo2@npm:^1.0.2": version: 1.0.2 resolution: "backo2@npm:1.0.2" checksum: fda8d0a0f4810068d23715f2f45153146d6ee8f62dd827ce1e0b6cc3c8328e84ad61e11399a83931705cef702fe7cbb457856bf99b9bd10c4ed57b0786252385 @@ -17754,13 +17303,6 @@ __metadata: languageName: node linkType: hard -"base64-arraybuffer@npm:0.1.4": - version: 0.1.4 - resolution: "base64-arraybuffer@npm:0.1.4" - checksum: d249a929e27b2430d7ba1527e91a36e14da37ae2f80e350c4d696a038257718f8da07577e820e7262f86a0ecd573c283db10c46502080f53ae11bfdd99b6a029 - languageName: node - linkType: hard - "base64-js@npm:^1.0.2, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -17775,13 +17317,6 @@ __metadata: languageName: node linkType: hard -"base64id@npm:2.0.0": - version: 2.0.0 - resolution: "base64id@npm:2.0.0" - checksum: 581b1d37e6cf3738b7ccdd4d14fe2bfc5c238e696e2720ee6c44c183b838655842e22034e53ffd783f872a539915c51b0d4728a49c7cc678ac5a758e00d62168 - languageName: node - linkType: hard - "base64url@npm:3.x.x": version: 3.0.1 resolution: "base64url@npm:3.0.1" @@ -17943,13 +17478,6 @@ __metadata: languageName: node linkType: hard -"blob@npm:0.0.5": - version: 0.0.5 - resolution: "blob@npm:0.0.5" - checksum: ca6a025f1108b7fd317fba0a0d64424f5cf47be6f4653ec8ba44777804a4e5c86c8d7a7e41f75fe52fd77c5bfc3479f68e017abe649a241c15c3fc07bfc59e7b - languageName: node - linkType: hard - "bluebird@npm:3.7.2, bluebird@npm:^3.3.5, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": version: 3.7.2 resolution: "bluebird@npm:3.7.2" @@ -17957,13 +17485,6 @@ __metadata: languageName: node linkType: hard -"bmp-js@npm:^0.1.0": - version: 0.1.0 - resolution: "bmp-js@npm:0.1.0" - checksum: 2f6cf7eeabae2aa50eb768122f59e9752caa97248028cb8b5cf0d9db7cf8fb3a60262aeb2c6889dd21357ab061b2fb318f21f20d2b24963ba36ead8e264c6654 - languageName: node - linkType: hard - "bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.1.1, bn.js@npm:^4.11.9": version: 4.12.0 resolution: "bn.js@npm:4.12.0" @@ -18211,13 +17732,6 @@ __metadata: languageName: node linkType: hard -"buffer-equal@npm:0.0.1": - version: 0.0.1 - resolution: "buffer-equal@npm:0.0.1" - checksum: ca4b52e6c01143529d957a78cb9a93e4257f172bbab30d9eb87c20ae085ed23c5e07f236ac051202dacbf3d17aba42e1455f84cba21ea79b67d57f2b05e9a613 - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -18260,7 +17774,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.0, buffer@npm:^5.2.1, buffer@npm:^5.5.0, buffer@npm:^5.6.0": +"buffer@npm:^5.2.1, buffer@npm:^5.5.0, buffer@npm:^5.6.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -19187,7 +18701,7 @@ __metadata: languageName: node linkType: hard -"color@npm:^4.0.1": +"color@npm:^4.0.1, color@npm:^4.2.3": version: 4.2.3 resolution: "color@npm:4.2.3" dependencies: @@ -19377,34 +18891,13 @@ __metadata: languageName: node linkType: hard -"component-bind@npm:1.0.0": - version: 1.0.0 - resolution: "component-bind@npm:1.0.0" - checksum: 746c5810b9f8735643840ad04072e1ab817444d44dc1aadc813f1f1a17c47c27616584caa0db93db7e687bfe73b65073d8246c785bcdac80f8f3627d3bb26883 - languageName: node - linkType: hard - -"component-emitter@npm:1.2.1": - version: 1.2.1 - resolution: "component-emitter@npm:1.2.1" - checksum: 00599b827635cab65bb20e5e3e2db4cea120b76b6626ce3ac6c85d7f5f39bbadd9fec530da444380035dd1c8ff08f9badca54d40b68feaf74bc64f75d537ef61 - languageName: node - linkType: hard - -"component-emitter@npm:^1.3.0, component-emitter@npm:~1.3.0": +"component-emitter@npm:^1.3.0": version: 1.3.0 resolution: "component-emitter@npm:1.3.0" checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b languageName: node linkType: hard -"component-inherit@npm:0.0.3": - version: 0.0.3 - resolution: "component-inherit@npm:0.0.3" - checksum: 9f5b872a6b3a396cf827d471d07db5626d7918202ab6c1d59f2b849bab18ce4fe61d9b2b7b6ae5cd547b81f27a3de5ae678f438bfe4fadce8f26a1526ba86a74 - languageName: node - linkType: hard - "compress-commons@npm:^4.1.0": version: 4.1.0 resolution: "compress-commons@npm:4.1.0" @@ -19648,7 +19141,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.4.2, cookie@npm:^0.4.2, cookie@npm:~0.4.1": +"cookie@npm:0.4.2, cookie@npm:^0.4.2": version: 0.4.2 resolution: "cookie@npm:0.4.2" checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b @@ -19709,7 +19202,7 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^3.4.1, core-js@npm:^3.6.5": +"core-js@npm:^3.6.5": version: 3.27.2 resolution: "core-js@npm:3.27.2" checksum: 718debd426f55a6b97cf9b757c936be258afd6d4f7052f89d0f96c982d7013e9000b0b006df42831a0cf32adad298e34d6a19052dce9ae1c7ab87162c0c665e0 @@ -20263,31 +19756,6 @@ __metadata: languageName: node linkType: hard -"cypress-plugin-snapshots@npm:^1.4.4": - version: 1.4.4 - resolution: "cypress-plugin-snapshots@npm:1.4.4" - dependencies: - diff2html: ^2.7.0 - fs-extra: ^7.0.1 - image-size: ^0.7.2 - jimp: ^0.10.3 - js-base64: ^2.5.1 - lodash: ^4.17.13 - pixelmatch: ^4.0.2 - pngjs: ^3.3.3 - prettier: ^1.16.4 - rimraf: ^2.6.3 - sanitize-filename: ^1.6.1 - socket.io: ^2.2.0 - socket.io-client: ^2.2.0 - source-map-support: ^0.5.10 - unidiff: 1.0.2 - peerDependencies: - cypress: ^4.5.0 - checksum: 75b8d6b6e0353f7d2ac617c503f33c84c7c5f4e650dbaf59a76558e971bcc82532d2ac37b9eeccb7382354a13fe5d0068c8e97a9f7f6f1af6135cf5286c285f9 - languageName: node - linkType: hard - "cypress@npm:^10.0.0": version: 10.11.0 resolution: "cypress@npm:10.11.0" @@ -20655,24 +20123,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:~3.1.0": - version: 3.1.0 - resolution: "debug@npm:3.1.0" - dependencies: - ms: 2.0.0 - checksum: 0b52718ab957254a5b3ca07fc34543bc778f358620c206a08452251eb7fc193c3ea3505072acbf4350219c14e2d71ceb7bdaa0d3370aa630b50da790458d08b3 - languageName: node - linkType: hard - -"debug@npm:~4.1.0": - version: 4.1.1 - resolution: "debug@npm:4.1.1" - dependencies: - ms: ^2.1.1 - checksum: 1e681f5cce94ba10f8dde74b20b42e4d8cf0d2a6700f4c165bb3bb6885565ef5ca5885bf07e704974a835f2415ff095a63164f539988a1f07e8a69fe8b1d65ad - languageName: node - linkType: hard - "debuglog@npm:^1.0.1": version: 1.0.1 resolution: "debuglog@npm:1.0.1" @@ -20966,7 +20416,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1": version: 2.0.1 resolution: "detect-libc@npm:2.0.1" checksum: ccb05fcabbb555beb544d48080179c18523a343face9ee4e1a86605a8715b4169f94d663c21a03c310ac824592f2ba9a5270218819bb411ad7be578a527593d7 @@ -21031,18 +20481,6 @@ __metadata: languageName: node linkType: hard -"diff2html@npm:^2.7.0": - version: 2.12.2 - resolution: "diff2html@npm:2.12.2" - dependencies: - diff: ^4.0.1 - hogan.js: ^3.0.2 - merge: ^1.2.1 - whatwg-fetch: ^3.0.0 - checksum: 4d659fb558575406881bc9e6b892a236d038bca1335cbdde65ebfde462cf1cff7a7ab4a2e82d9874205e658dd2caa31699ba24ea4efb714d7c7277ddb96c7768 - languageName: node - linkType: hard - "diff3@npm:0.0.3": version: 0.0.3 resolution: "diff3@npm:0.0.3" @@ -21050,13 +20488,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^2.2.2": - version: 2.2.3 - resolution: "diff@npm:2.2.3" - checksum: b5b6fbe1848c621ef6f6c154e689842f7bb992b4c0ae257b70f5577b0906812537bf2eccda14d0215f90579567c8880eb44f1cf3851bd615583214a4f4d61ce3 - languageName: node - linkType: hard - "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -21223,13 +20654,6 @@ __metadata: languageName: node linkType: hard -"dom-walk@npm:^0.1.0": - version: 0.1.1 - resolution: "dom-walk@npm:0.1.1" - checksum: f55db10d0344b87cca1046a2356915a8e9ddee90fc97d3ebb26c4021e593cce47d24cbef18aac721421787c6a60fa14dd079a0b81f8610c9aeeec52177fd2acf - languageName: node - linkType: hard - "domain-browser@npm:^1.1.1": version: 1.2.0 resolution: "domain-browser@npm:1.2.0" @@ -21554,52 +20978,6 @@ __metadata: languageName: node linkType: hard -"engine.io-client@npm:~3.5.0": - version: 3.5.2 - resolution: "engine.io-client@npm:3.5.2" - dependencies: - component-emitter: ~1.3.0 - component-inherit: 0.0.3 - debug: ~3.1.0 - engine.io-parser: ~2.2.0 - has-cors: 1.1.0 - indexof: 0.0.1 - parseqs: 0.0.6 - parseuri: 0.0.6 - ws: ~7.4.2 - xmlhttprequest-ssl: ~1.6.2 - yeast: 0.1.2 - checksum: 2a4a8407ea939c45826d3483e39fe017e66d488dfd101282a79644f75196f7b84cac9d6f981e3ace1ab231e2469c18b25d79b114f20ab58d0eaa393bf66f213e - languageName: node - linkType: hard - -"engine.io-parser@npm:~2.2.0": - version: 2.2.1 - resolution: "engine.io-parser@npm:2.2.1" - dependencies: - after: 0.8.2 - arraybuffer.slice: ~0.0.7 - base64-arraybuffer: 0.1.4 - blob: 0.0.5 - has-binary2: ~1.0.2 - checksum: c7291955c1af4b6c384c2642c2d10a4df0ca7b6acc5cb178dd4933bcb1522eccc3d89ffae6e6080ce760451378fce49db47af3fb9999cae08de310e75f3c29d6 - languageName: node - linkType: hard - -"engine.io@npm:~3.6.0": - version: 3.6.1 - resolution: "engine.io@npm:3.6.1" - dependencies: - accepts: ~1.3.4 - base64id: 2.0.0 - cookie: ~0.4.1 - debug: ~4.1.0 - engine.io-parser: ~2.2.0 - ws: ~7.4.2 - checksum: 7dd9eddc79aedf61a63141e6208ee89936c659445378f965bff6d5eb3e9cb4815aff735f2b1886487e2be51116a343e89d972ae5790bf2ac9fdcb2572baf8935 - languageName: node - linkType: hard - "enhanced-resolve@npm:^5.10.0": version: 5.10.0 resolution: "enhanced-resolve@npm:5.10.0" @@ -22913,13 +22291,6 @@ __metadata: languageName: node linkType: hard -"exif-parser@npm:^0.1.12": - version: 0.1.12 - resolution: "exif-parser@npm:0.1.12" - checksum: 6ba50cb9e0b45a6efa37e966a9582ecd171b5c5b3ef0c47542f2b862c521f70d2f656dde85b4d2a5dd8e1163486b09049f4c412e9c6176bfbda1654a5b2f021c - languageName: node - linkType: hard - "exit-on-epipe@npm:~1.0.1": version: 1.0.1 resolution: "exit-on-epipe@npm:1.0.1" @@ -23365,13 +22736,6 @@ __metadata: languageName: node linkType: hard -"file-type@npm:^9.0.0": - version: 9.0.0 - resolution: "file-type@npm:9.0.0" - checksum: 9ea78b29c3762d967eb1e3e4f45e401388b6d252b12c217f78f5ea97556ff7e35e4c7255cab68810ac414d51b776bd4e83504c86f132c262a454251561189efa - languageName: node - linkType: hard - "file-uri-to-path@npm:1.0.0": version: 1.0.0 resolution: "file-uri-to-path@npm:1.0.0" @@ -24200,6 +23564,19 @@ __metadata: languageName: node linkType: hard +"glob@npm:8.0.3": + version: 8.0.3 + resolution: "glob@npm:8.0.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^5.0.1 + once: ^1.3.0 + checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 + languageName: node + linkType: hard + "glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -24270,16 +23647,6 @@ __metadata: languageName: node linkType: hard -"global@npm:~4.4.0": - version: 4.4.0 - resolution: "global@npm:4.4.0" - dependencies: - min-document: ^2.19.0 - process: ^0.11.10 - checksum: 9c057557c8f5a5bcfbeb9378ba4fe2255d04679452be504608dd5f13b54edf79f7be1db1031ea06a4ec6edd3b9f5f17d2d172fb47e6c69dae57fd84b7e72b77f - languageName: node - linkType: hard - "globals@npm:^11.1.0, globals@npm:^11.12.0": version: 11.12.0 resolution: "globals@npm:11.12.0" @@ -24705,22 +24072,6 @@ __metadata: languageName: node linkType: hard -"has-binary2@npm:~1.0.2": - version: 1.0.3 - resolution: "has-binary2@npm:1.0.3" - dependencies: - isarray: 2.0.1 - checksum: 9183a617830b1f50b35961fbe39278cc43089cdac7c47a7f16aafb9d8190553fa360e5da34a479d6c988db40ea10aa02bca5abcb2451b5fc3924ae2f013ffdab - languageName: node - linkType: hard - -"has-cors@npm:1.1.0": - version: 1.1.0 - resolution: "has-cors@npm:1.1.0" - checksum: 549ce94113fd23895b22d71ade9809918577b8558cd4d701fe79045d8b1d58d87eba870260b28f6a3229be933a691c55653afd496d0fc52e98fd2ff577f01197 - languageName: node - linkType: hard - "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -24897,18 +24248,6 @@ __metadata: languageName: node linkType: hard -"hogan.js@npm:^3.0.2": - version: 3.0.2 - resolution: "hogan.js@npm:3.0.2" - dependencies: - mkdirp: 0.3.0 - nopt: 1.0.10 - bin: - hulk: ./bin/hulk - checksum: c7bbff84faa9ca265c39f4a2100546ba0388fcc9c5bac8526f488592ce3fcaa042eba6ac25db277f4478ec3855b9bc28ce59acffbf6e8a28d45a17df7590c6aa - languageName: node - linkType: hard - "hoist-non-react-statics@npm:^2.3.1": version: 2.5.5 resolution: "hoist-non-react-statics@npm:2.5.5" @@ -25364,15 +24703,6 @@ __metadata: languageName: node linkType: hard -"image-size@npm:^0.7.2": - version: 0.7.5 - resolution: "image-size@npm:0.7.5" - bin: - image-size: bin/image-size.js - checksum: f88860c9d9b5c8ad00d3de9d6f5ba105bda5a5024bfb6b90559a075a4b838ed4f5d3cba14edf0f18fe5d75df596a172b52feca43848e11c34f31f4df2c88a011 - languageName: node - linkType: hard - "immer@npm:9.0.7": version: 9.0.7 resolution: "immer@npm:9.0.7" @@ -25487,13 +24817,6 @@ __metadata: languageName: node linkType: hard -"indexof@npm:0.0.1": - version: 0.0.1 - resolution: "indexof@npm:0.0.1" - checksum: 0fb04e8b147b8585d981a6df1564f25bb3678d6fa74e33e5cecc1464b10f78e15e8ef6bb688f135fe5c2844a128fac8a7831cbe5adc81fdcf12681b093dfcc25 - languageName: node - linkType: hard - "infer-owner@npm:^1.0.4": version: 1.0.4 resolution: "infer-owner@npm:1.0.4" @@ -25940,13 +25263,6 @@ __metadata: languageName: node linkType: hard -"is-function@npm:^1.0.1": - version: 1.0.2 - resolution: "is-function@npm:1.0.2" - checksum: 7d564562e07b4b51359547d3ccc10fb93bb392fd1b8177ae2601ee4982a0ece86d952323fc172a9000743a3971f09689495ab78a1d49a9b14fc97a7e28521dc0 - languageName: node - linkType: hard - "is-generator-fn@npm:^2.0.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" @@ -26386,13 +25702,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:2.0.1": - version: 2.0.1 - resolution: "isarray@npm:2.0.1" - checksum: a0521973213019b82889f028a41528ce7a3f103a710357b6875066202ee5a1fa9ae4135f915f3be9160b8c7a3ae4b39b073b804da3b58fa6b72f8b4b1e3dfef5 - languageName: node - linkType: hard - "isarray@npm:^1.0.0, isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" @@ -27166,20 +26475,6 @@ __metadata: languageName: node linkType: hard -"jimp@npm:^0.10.3": - version: 0.10.3 - resolution: "jimp@npm:0.10.3" - dependencies: - "@babel/runtime": ^7.7.2 - "@jimp/custom": ^0.10.3 - "@jimp/plugins": ^0.10.3 - "@jimp/types": ^0.10.3 - core-js: ^3.4.1 - regenerator-runtime: ^0.13.3 - checksum: ace7d9d76c2802e26392603c1fdfc988e7c468941ac127405f6752db9b5c89e1e27f202f96b764eede001ac245be0b46f52bb87eb91a42374027050568a7e816 - languageName: node - linkType: hard - "jju@npm:~1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -27228,20 +26523,6 @@ __metadata: languageName: node linkType: hard -"jpeg-js@npm:^0.3.4": - version: 0.3.7 - resolution: "jpeg-js@npm:0.3.7" - checksum: 85a1ab09fe696fdd7b94ee077f1b8d1de307ffd7a83c0c45cc5cc56b0d7b47e561b92f6f090165648387875d674ba3076e3fedf395d5be4ef46760b6ebc804fe - languageName: node - linkType: hard - -"js-base64@npm:^2.5.1": - version: 2.6.4 - resolution: "js-base64@npm:2.6.4" - checksum: 5f4084078d6c46f8529741d110df84b14fac3276b903760c21fa8cc8521370d607325dfe1c1a9fbbeaae1ff8e602665aaeef1362427d8fef704f9e3659472ce8 - languageName: node - linkType: hard - "js-base64@npm:^3.6.0": version: 3.7.4 resolution: "js-base64@npm:3.7.4" @@ -28410,22 +27691,6 @@ __metadata: languageName: node linkType: hard -"load-bmfont@npm:^1.3.1, load-bmfont@npm:^1.4.0": - version: 1.4.1 - resolution: "load-bmfont@npm:1.4.1" - dependencies: - buffer-equal: 0.0.1 - mime: ^1.3.4 - parse-bmfont-ascii: ^1.0.3 - parse-bmfont-binary: ^1.0.5 - parse-bmfont-xml: ^1.1.4 - phin: ^2.9.1 - xhr: ^2.0.1 - xtend: ^4.0.0 - checksum: 688d932fb0dc4c9333747736ccd926261f0b91734b7bdb6ff24f8659ef068a0f0b2278084b208851afac0beec79af7bd6664fe2ed5b6c5e1db88755fc25f785e - languageName: node - linkType: hard - "load-json-file@npm:^1.0.0": version: 1.1.0 resolution: "load-json-file@npm:1.1.0" @@ -28730,7 +27995,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.13, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15": +"lodash@npm:4.17.21, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -29399,13 +28664,6 @@ __metadata: languageName: node linkType: hard -"merge@npm:^1.2.1": - version: 1.2.1 - resolution: "merge@npm:1.2.1" - checksum: 2298c4fdcf64561f320b92338681f7ffcafafb579a6e294066ae3e7bd10ae25df363903d2f028072733b9f79a1f75d2b999aef98ad5d73de13641da39cda0913 - languageName: node - linkType: hard - "meros@npm:1.2.1, meros@npm:^1.1.4": version: 1.2.1 resolution: "meros@npm:1.2.1" @@ -29418,6 +28676,13 @@ __metadata: languageName: node linkType: hard +"meta-png@npm:1.0.5": + version: 1.0.5 + resolution: "meta-png@npm:1.0.5" + checksum: b8c92a696d5e78ef7f0ab53eb9039f108ee0c55cdac9d70e92e5fe2187b090176c0489426d58e594c7d1096481dac37913dd1265f28dfa39926db4d702a5e21d + languageName: node + linkType: hard + "methods@npm:^1.0.0, methods@npm:^1.1.2, methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -29807,7 +29072,7 @@ __metadata: languageName: node linkType: hard -"mime@npm:1.6.0, mime@npm:^1.3.4": +"mime@npm:1.6.0": version: 1.6.0 resolution: "mime@npm:1.6.0" bin: @@ -29869,15 +29134,6 @@ __metadata: languageName: node linkType: hard -"min-document@npm:^2.19.0": - version: 2.19.0 - resolution: "min-document@npm:2.19.0" - dependencies: - dom-walk: ^0.1.0 - checksum: da6437562ea2228041542a2384528e74e22d1daa1a4ec439c165abf0b9d8a63e17e3b8a6dc6e0c731845e85301198730426932a0e813d23f932ca668340c9623 - languageName: node - linkType: hard - "min-indent@npm:^1.0.0": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -30093,24 +29349,6 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:0.3.0": - version: 0.3.0 - resolution: "mkdirp@npm:0.3.0" - checksum: 3ec9cda8bd89b64892728e5092bc79e88382e444d4bbde040c2fb8d7034dc70682cfdd729e93241fd5243d2397324c420ef68c717d806db51bf96c0fc80f4b1d - languageName: node - linkType: hard - -"mkdirp@npm:^0.5.1": - version: 0.5.5 - resolution: "mkdirp@npm:0.5.5" - dependencies: - minimist: ^1.2.5 - bin: - mkdirp: bin/cmd.js - checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d - languageName: node - linkType: hard - "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -30147,6 +29385,15 @@ __metadata: languageName: node linkType: hard +"move-file@npm:2.1.0": + version: 2.1.0 + resolution: "move-file@npm:2.1.0" + dependencies: + path-exists: ^4.0.0 + checksum: 2b92bbe047a302b593fcb2c0bf1131bb090ec80b3264569fc80d782c8ff829eecc13573943fa4d804c9747dec612ef2d1e84a5cfcf29cbc64a69ffcbb7ea09b3 + languageName: node + linkType: hard + "mri@npm:1.1.4": version: 1.1.4 resolution: "mri@npm:1.1.4" @@ -30453,6 +29700,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: latest + checksum: 2508bd2d2981945406243a7bd31362fc7af8b70b8b4d65f869c61731800058fb818cc2fd36c8eac714ddd0e568cc85becf5e165cebbdf7b5024d5151bbc75ea1 + languageName: node + linkType: hard + "node-cache@npm:^5.1.2": version: 5.1.2 resolution: "node-cache@npm:5.1.2" @@ -30648,17 +29904,6 @@ __metadata: languageName: node linkType: hard -"nopt@npm:1.0.10, nopt@npm:~1.0.10": - version: 1.0.10 - resolution: "nopt@npm:1.0.10" - dependencies: - abbrev: 1 - bin: - nopt: ./bin/nopt.js - checksum: f62575aceaa3be43f365bf37a596b89bbac2e796b001b6d2e2a85c2140a4e378ff919e2753ccba959c4fd344776fc88c29b393bc167fa939fb1513f126f4cd45 - languageName: node - linkType: hard - "nopt@npm:^5.0.0": version: 5.0.0 resolution: "nopt@npm:5.0.0" @@ -30681,6 +29926,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:~1.0.10": + version: 1.0.10 + resolution: "nopt@npm:1.0.10" + dependencies: + abbrev: 1 + bin: + nopt: ./bin/nopt.js + checksum: f62575aceaa3be43f365bf37a596b89bbac2e796b001b6d2e2a85c2140a4e378ff919e2753ccba959c4fd344776fc88c29b393bc167fa939fb1513f126f4cd45 + languageName: node + linkType: hard + "normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" @@ -31084,13 +30340,6 @@ __metadata: languageName: node linkType: hard -"omggif@npm:^1.0.9": - version: 1.0.10 - resolution: "omggif@npm:1.0.10" - checksum: 15102e46b6fa0fba32d7e948f702623cdc3cdcdfd64b2d33c6e29a61f366ffd0f250da55d66f5217dce5b93ba9c67763fa998652791a5c7f2201a3bde2c4db45 - languageName: node - linkType: hard - "on-finished@npm:2.4.1, on-finished@npm:^2.3.0": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -31473,7 +30722,7 @@ __metadata: languageName: node linkType: hard -"pako@npm:^1.0.10, pako@npm:^1.0.5, pako@npm:~1.0.5": +"pako@npm:^1.0.10, pako@npm:~1.0.5": version: 1.0.11 resolution: "pako@npm:1.0.11" checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 @@ -31520,30 +30769,6 @@ __metadata: languageName: node linkType: hard -"parse-bmfont-ascii@npm:^1.0.3": - version: 1.0.6 - resolution: "parse-bmfont-ascii@npm:1.0.6" - checksum: de3f6671f183c3e9d64bb4812b0407693b5fd0d24e9d16b2e106bb9eef809d64a6cc061f39ca29bb10c5c2e47e241e91b7aeefa587391fff7ccb27ab9db5012e - languageName: node - linkType: hard - -"parse-bmfont-binary@npm:^1.0.5": - version: 1.0.6 - resolution: "parse-bmfont-binary@npm:1.0.6" - checksum: ca37fb1e92f5941fddc5342b45857fafd27f00d2bd5fa44dd504bec6faeab97536c95ad45260c2dd5fc4c63de71e525663d3cdac09d038cbca803d97c669add5 - languageName: node - linkType: hard - -"parse-bmfont-xml@npm:^1.1.4": - version: 1.1.4 - resolution: "parse-bmfont-xml@npm:1.1.4" - dependencies: - xml-parse-from-string: ^1.0.0 - xml2js: ^0.4.5 - checksum: 879e5435be44f22b8c4934e2e1d2754a6d90a9ddb16309360daff965e1428d877b673f3d1fafaab4fef437c912a0db9f85545e0dd375ec62df7d4d328450d257 - languageName: node - linkType: hard - "parse-conflict-json@npm:^2.0.1": version: 2.0.1 resolution: "parse-conflict-json@npm:2.0.1" @@ -31594,13 +30819,6 @@ __metadata: languageName: node linkType: hard -"parse-headers@npm:^2.0.0": - version: 2.0.4 - resolution: "parse-headers@npm:2.0.4" - checksum: 29519ac013e100c11a67d0fc64eb33ae86523abf547f71dba36d484dcd16a2835dd11f31303f4ded27c40133dca5a5fe4d15b77f49091e470a6f74a023c59c4a - languageName: node - linkType: hard - "parse-json@npm:^2.2.0": version: 2.2.0 resolution: "parse-json@npm:2.2.0" @@ -31673,20 +30891,6 @@ __metadata: languageName: node linkType: hard -"parseqs@npm:0.0.6": - version: 0.0.6 - resolution: "parseqs@npm:0.0.6" - checksum: 7fc4ff4ba59764060bb8529875f6d4313056ea6939ff579b22dd7bd6f6033035e1fd2d6a559ab48ef0a7fa29a9d7731c982bfd1594e9115141fe1c328485ce9e - languageName: node - linkType: hard - -"parseuri@npm:0.0.6": - version: 0.0.6 - resolution: "parseuri@npm:0.0.6" - checksum: fa430e40f0c75293a28e5f1023da5f51a5038d5e34c48c517b0d5187143f6bcc67d3091a062b68765db4a22757e488c7d15854f9d1921f2c2b9afa5ca0629a84 - languageName: node - linkType: hard - "parseurl@npm:^1.3.3, parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -32177,13 +31381,6 @@ __metadata: languageName: node linkType: hard -"phin@npm:^2.9.1": - version: 2.9.3 - resolution: "phin@npm:2.9.3" - checksum: 7e2abd7be74a54eb7be92dccb1d7a019725c8adaa79ac22a38f25220f9a859393e654ea753a559d326aed7bbc966fadac88270cc8c39d78896f7784219560c47 - languageName: node - linkType: hard - "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -32300,14 +31497,14 @@ __metadata: languageName: node linkType: hard -"pixelmatch@npm:^4.0.2": - version: 4.0.2 - resolution: "pixelmatch@npm:4.0.2" +"pixelmatch@npm:5.3.0": + version: 5.3.0 + resolution: "pixelmatch@npm:5.3.0" dependencies: - pngjs: ^3.0.0 + pngjs: ^6.0.0 bin: pixelmatch: bin/pixelmatch - checksum: 9c5c1329001938cae6d01e2bb84a909ba767f8256bcafc075422cea2a4dbaa8bebd44fceaa4b4ce7cdc36d11f20d4f1ba0cf669851d5649b32d8d1d27e4f5a36 + checksum: f542713d89536551181ad9ddb666a1792ba00a8632d831093232a075cb3ccac05856e7a453ed7d0a41aaef64dcb5962e8ae5cbe646dd2761790d8ee51b0a0743 languageName: node linkType: hard @@ -32359,10 +31556,10 @@ __metadata: languageName: node linkType: hard -"pngjs@npm:^3.0.0, pngjs@npm:^3.3.3": - version: 3.4.0 - resolution: "pngjs@npm:3.4.0" - checksum: 8bd40bd698abd16b72c97b85cb858c80894fbedc76277ce72a784aa441e14795d45d9856e97333ca469b34b67528860ffc8a7317ca6beea349b645366df00bcd +"pngjs@npm:6.0.0, pngjs@npm:^6.0.0": + version: 6.0.0 + resolution: "pngjs@npm:6.0.0" + checksum: ab6c285086060087097eab9fe6b5a528a24f9e79c03dea2b4fd6264ed4fdb5beff4a3257eeeaf2a9dc18249b539609c2a4e4013c567164a1f6b5ba2c974d5ecb languageName: node linkType: hard @@ -32852,6 +32049,28 @@ __metadata: languageName: node linkType: hard +"prebuild-install@npm:^7.1.1": + version: 7.1.1 + resolution: "prebuild-install@npm:7.1.1" + dependencies: + detect-libc: ^2.0.0 + expand-template: ^2.0.3 + github-from-package: 0.0.0 + minimist: ^1.2.3 + mkdirp-classic: ^0.5.3 + napi-build-utils: ^1.0.1 + node-abi: ^3.3.0 + pump: ^3.0.0 + rc: ^1.2.7 + simple-get: ^4.0.0 + tar-fs: ^2.0.0 + tunnel-agent: ^0.6.0 + bin: + prebuild-install: bin.js + checksum: dbf96d0146b6b5827fc8f67f72074d2e19c69628b9a7a0a17d0fad1bf37e9f06922896972e074197fc00a52eae912993e6ef5a0d471652f561df5cb516f3f467 + languageName: node + linkType: hard + "precond@npm:0.2": version: 0.2.3 resolution: "precond@npm:0.2.3" @@ -32885,15 +32104,6 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^1.16.4": - version: 1.19.1 - resolution: "prettier@npm:1.19.1" - bin: - prettier: ./bin-prettier.js - checksum: bc78219e0f8173a808f4c6c8e0a137dd8ebd4fbe013e63fe1a37a82b48612f17b8ae8e18a992adf802ee2cf7428f14f084e7c2846ca5759cf4013c6e54810e1f - languageName: node - linkType: hard - "prettier@npm:^2.2.1, prettier@npm:^2.7.1": version: 2.8.1 resolution: "prettier@npm:2.8.1" @@ -34396,7 +33606,7 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.13.11, regenerator-runtime@npm:^0.13.3, regenerator-runtime@npm:^0.13.4": +"regenerator-runtime@npm:^0.13.11, regenerator-runtime@npm:^0.13.4": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 @@ -35234,15 +34444,6 @@ __metadata: languageName: node linkType: hard -"sanitize-filename@npm:^1.6.1": - version: 1.6.3 - resolution: "sanitize-filename@npm:1.6.3" - dependencies: - truncate-utf8-bytes: ^1.0.0 - checksum: aa733c012b7823cf65730603cf3b503c641cee6b239771d3164ca482f22d81a50e434a713938d994071db18e4202625669cc56bccc9d13d818b4c983b5f47fde - languageName: node - linkType: hard - "sax@npm:1.2.1": version: 1.2.1 resolution: "sax@npm:1.2.1" @@ -35621,6 +34822,23 @@ __metadata: languageName: node linkType: hard +"sharp@npm:0.31.2": + version: 0.31.2 + resolution: "sharp@npm:0.31.2" + dependencies: + color: ^4.2.3 + detect-libc: ^2.0.1 + node-addon-api: ^5.0.0 + node-gyp: latest + prebuild-install: ^7.1.1 + semver: ^7.3.8 + simple-get: ^4.0.1 + tar-fs: ^2.1.1 + tunnel-agent: ^0.6.0 + checksum: 076717b7a073ea47bb522ff2931b74b6608daeb6f7ae334e4848d47fdf4d23bcb18cd49044fd5fb27ef27a1a4aa87d141894d67d1c4bb15a6e2e63cf4dbe329e + languageName: node + linkType: hard + "shebang-command@npm:^1.2.0": version: 1.2.0 resolution: "shebang-command@npm:1.2.0" @@ -35864,68 +35082,6 @@ __metadata: languageName: node linkType: hard -"socket.io-adapter@npm:~1.1.0": - version: 1.1.2 - resolution: "socket.io-adapter@npm:1.1.2" - checksum: 8e18df7f8c471001b65e43542c5c743b63f041781bca097b4a407dd199dd757c14c0d470992f84923f48e75729a2ac0ae634fec820f20881cadae8a495bf68e9 - languageName: node - linkType: hard - -"socket.io-client@npm:2.5.0, socket.io-client@npm:^2.2.0": - version: 2.5.0 - resolution: "socket.io-client@npm:2.5.0" - dependencies: - backo2: 1.0.2 - component-bind: 1.0.0 - component-emitter: ~1.3.0 - debug: ~3.1.0 - engine.io-client: ~3.5.0 - has-binary2: ~1.0.2 - indexof: 0.0.1 - parseqs: 0.0.6 - parseuri: 0.0.6 - socket.io-parser: ~3.3.0 - to-array: 0.1.4 - checksum: a33accaf02c4a4c47ac9b09018fa4b9fa67a648a0cc92602e520ec884520050796786eb0df78fc1a9bf51e7b3d7b50ba4ffd3bda6e44df2c253a77100124669c - languageName: node - linkType: hard - -"socket.io-parser@npm:~3.3.0": - version: 3.3.3 - resolution: "socket.io-parser@npm:3.3.3" - dependencies: - component-emitter: ~1.3.0 - debug: ~3.1.0 - isarray: 2.0.1 - checksum: 6cf464e324e4207811dbf994822c0aa18ba01d615fbed8e3f069927e0e402fdc9b3ac3d2657f18d481fb8c584b1fd40152ced15b3686982a51ec843c6a76dbc2 - languageName: node - linkType: hard - -"socket.io-parser@npm:~3.4.0": - version: 3.4.1 - resolution: "socket.io-parser@npm:3.4.1" - dependencies: - component-emitter: 1.2.1 - debug: ~4.1.0 - isarray: 2.0.1 - checksum: f8bac61298375680aceb6c72ffbb47a2d950daa05e01a3b67d9330945073ae01e35aa131dcddd7e0b354ca302c51742bebaae57a50c188d4c1fe8af9461aa38d - languageName: node - linkType: hard - -"socket.io@npm:^2.2.0": - version: 2.5.0 - resolution: "socket.io@npm:2.5.0" - dependencies: - debug: ~4.1.0 - engine.io: ~3.6.0 - has-binary2: ~1.0.2 - socket.io-adapter: ~1.1.0 - socket.io-client: 2.5.0 - socket.io-parser: ~3.4.0 - checksum: 6f20a413352cb0a8bb13063249fe425c81fb54beca1bdd71221262305cf21232e6a37fdd4a232b5695dbd4d3204281a9313829c9c373c5ba01c5bd7218edd094 - languageName: node - linkType: hard - "sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" @@ -36003,7 +35159,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.10, source-map-support@npm:^0.5.16, source-map-support@npm:~0.5.20": +"source-map-support@npm:^0.5.16, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -37382,13 +36538,6 @@ __metadata: languageName: node linkType: hard -"timm@npm:^1.6.1": - version: 1.7.1 - resolution: "timm@npm:1.7.1" - checksum: c80df538ec7fae50a0e3183931b20fbe97f6f2c06907d9675eb7b9d90b3f788af7742285c730192db3b066c4ab22ebae75f8d21970c5b03f38d928d5bb2a0339 - languageName: node - linkType: hard - "timsort@npm:^0.3.0": version: 0.3.0 resolution: "timsort@npm:0.3.0" @@ -37424,13 +36573,6 @@ __metadata: languageName: node linkType: hard -"tinycolor2@npm:^1.4.1": - version: 1.4.2 - resolution: "tinycolor2@npm:1.4.2" - checksum: 57ed262e08815a4ab0ed933edafdbc6555a17081781766149813b44a080ecbe58b3ee281e81c0e75b42e4d41679f138cfa98eabf043f829e0683c04adb12c031 - languageName: node - linkType: hard - "title-case@npm:^3.0.3": version: 3.0.3 resolution: "title-case@npm:3.0.3" @@ -37474,13 +36616,6 @@ __metadata: languageName: node linkType: hard -"to-array@npm:0.1.4": - version: 0.1.4 - resolution: "to-array@npm:0.1.4" - checksum: 396a04df5a5e74df5c8891ebb819d575d626e2929312fec3d649b4d38203bbf7185c270cb31fcad903564743895e935f2e965770bd1c6eb137dde48c4d60788d - languageName: node - linkType: hard - "to-arraybuffer@npm:^1.0.0": version: 1.0.1 resolution: "to-arraybuffer@npm:1.0.1" @@ -37654,15 +36789,6 @@ __metadata: languageName: node linkType: hard -"truncate-utf8-bytes@npm:^1.0.0": - version: 1.0.2 - resolution: "truncate-utf8-bytes@npm:1.0.2" - dependencies: - utf8-byte-length: ^1.0.1 - checksum: ad097314709ea98444ad9c80c03aac8da805b894f37ceb5685c49ad297483afe3a5ec9572ebcaff699dda72b6cd447a2ba2a3fd10e96c2628cd16d94abeb328a - languageName: node - linkType: hard - "tryer@npm:^1.0.1": version: 1.0.1 resolution: "tryer@npm:1.0.1" @@ -38126,15 +37252,6 @@ __metadata: languageName: node linkType: hard -"unidiff@npm:1.0.2": - version: 1.0.2 - resolution: "unidiff@npm:1.0.2" - dependencies: - diff: ^2.2.2 - checksum: 442d699d44a07f7511e2ec5b6433fb562d6ddce9eb91c6c28ff23bdda60595d9d7e8c6341937f18996ca03c198d93cc9b8fb448dde211ddeeb45bb45bff8ca1e - languageName: node - linkType: hard - "unified@npm:^10.0.0": version: 10.1.0 resolution: "unified@npm:10.1.0" @@ -38442,22 +37559,6 @@ __metadata: languageName: node linkType: hard -"utf8-byte-length@npm:^1.0.1": - version: 1.0.4 - resolution: "utf8-byte-length@npm:1.0.4" - checksum: f188ca076ec094d58e7009fcc32623c5830c7f0f3e15802bfa4fdd1e759454a481fc4ac05e0fa83b7736e77af628a9ee0e57dcc89683d688fde3811473e42143 - languageName: node - linkType: hard - -"utif@npm:^2.0.1": - version: 2.0.1 - resolution: "utif@npm:2.0.1" - dependencies: - pako: ^1.0.5 - checksum: 66b0bffc18f08834a34c44846f189b2223418e8bddfff3882e07fccea54436737f7334e27c86cbff46e247487dc92377b7a17428a7aafc5d5c733d61bed39038 - languageName: node - linkType: hard - "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -39116,7 +38217,7 @@ __metadata: languageName: node linkType: hard -"whatwg-fetch@npm:>=0.10.0, whatwg-fetch@npm:^3.0.0, whatwg-fetch@npm:^3.4.1": +"whatwg-fetch@npm:>=0.10.0, whatwg-fetch@npm:^3.4.1": version: 3.6.2 resolution: "whatwg-fetch@npm:3.6.2" checksum: ee976b7249e7791edb0d0a62cd806b29006ad7ec3a3d89145921ad8c00a3a67e4be8f3fb3ec6bc7b58498724fd568d11aeeeea1f7827e7e1e5eae6c8a275afed @@ -39435,21 +38536,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:~7.4.2": - version: 7.4.6 - resolution: "ws@npm:7.4.6" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 3a990b32ed08c72070d5e8913e14dfcd831919205be52a3ff0b4cdd998c8d554f167c9df3841605cde8b11d607768cacab3e823c58c96a5c08c987e093eb767a - languageName: node - linkType: hard - "xcase@npm:^2.0.1": version: 2.0.1 resolution: "xcase@npm:2.0.1" @@ -39457,18 +38543,6 @@ __metadata: languageName: node linkType: hard -"xhr@npm:^2.0.1": - version: 2.6.0 - resolution: "xhr@npm:2.6.0" - dependencies: - global: ~4.4.0 - is-function: ^1.0.1 - parse-headers: ^2.0.0 - xtend: ^4.0.0 - checksum: a1db277e37737caf3ed363d2a33ce4b4ea5b5fc190b663a6f70bc252799185b840ccaa166eaeeea4841c9c60b87741f0a24e29cbcf6708dd425986d4df186d2f - languageName: node - linkType: hard - "xml-but-prettier@npm:^1.0.1": version: 1.0.1 resolution: "xml-but-prettier@npm:1.0.1" @@ -39513,13 +38587,6 @@ __metadata: languageName: node linkType: hard -"xml-parse-from-string@npm:^1.0.0": - version: 1.0.1 - resolution: "xml-parse-from-string@npm:1.0.1" - checksum: 5155cb98e428409829f4060ce542c55438b2f7646d11fd306d850eaf12d35c06ffd9e86d76aa5230121a533b958fd1a319d6f90a5c113391853d0ff01f4da7bb - languageName: node - linkType: hard - "xml2js@npm:0.4.19": version: 0.4.19 resolution: "xml2js@npm:0.4.19" @@ -39530,7 +38597,7 @@ __metadata: languageName: node linkType: hard -"xml2js@npm:^0.4.11, xml2js@npm:^0.4.19, xml2js@npm:^0.4.23, xml2js@npm:^0.4.5": +"xml2js@npm:^0.4.11, xml2js@npm:^0.4.19, xml2js@npm:^0.4.23": version: 0.4.23 resolution: "xml2js@npm:0.4.23" dependencies: @@ -39582,13 +38649,6 @@ __metadata: languageName: node linkType: hard -"xmlhttprequest-ssl@npm:~1.6.2": - version: 1.6.3 - resolution: "xmlhttprequest-ssl@npm:1.6.3" - checksum: ac8e5de1cdd170bddb928de75393e8977e7eb80c0d8c24fe4be07f6aa1d5c8e2e42296d29abca6591ec2046cc708c220791ecfa56db43c958b8e4de8e7d39984 - languageName: node - linkType: hard - "xpath@npm:0.0.32": version: 0.0.32 resolution: "xpath@npm:0.0.32" @@ -39793,13 +38853,6 @@ __metadata: languageName: node linkType: hard -"yeast@npm:0.1.2": - version: 0.1.2 - resolution: "yeast@npm:0.1.2" - checksum: 81a250b69f601fed541e9518eb2972e75631dd81231689503d7f288612d4eec793b29c208d6807fd6bfc4c2a43614d0c6db233739a4ae6223e244aaed6a885c0 - languageName: node - linkType: hard - "yeoman-environment@npm:^3.9.1": version: 3.13.0 resolution: "yeoman-environment@npm:3.13.0" From 7e9536ada8ea3c288ce8633bd5637f16ca5f30b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Wed, 7 Dec 2022 14:13:19 +0100 Subject: [PATCH 043/130] add the new package @frsource/cypress-plugin-visual-regression-diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- yarn.lock | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7844d0fadb..5b268ef6fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,6 +1,3 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - __metadata: version: 6 cacheKey: 8 @@ -13401,7 +13398,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-techdocs-node": "workspace:^" - "@frsource/cypress-plugin-visual-regression-diff": ^3.2.8 + "@frsource/cypress-plugin-visual-regression-diff": ^3.2.4 "@types/commander": ^2.12.2 "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 From f08ee791f39bdd7d03b0bb4634da6183e3c4593c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Wed, 7 Dec 2022 14:18:41 +0100 Subject: [PATCH 044/130] update to cypress 10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- packages/techdocs-cli/cypress.config.js | 16 ++++++++++ .../techdocs-cli/cypress/plugins/index.js | 30 +++++++++++++++++++ yarn.lock | 5 +++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 packages/techdocs-cli/cypress/plugins/index.js diff --git a/packages/techdocs-cli/cypress.config.js b/packages/techdocs-cli/cypress.config.js index c4b007634a..b33ab2eace 100644 --- a/packages/techdocs-cli/cypress.config.js +++ b/packages/techdocs-cli/cypress.config.js @@ -21,9 +21,25 @@ module.exports = defineConfig({ setupNodeEvents(on, config) { initPlugin(on, config); }, +<<<<<<< HEAD excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], }, +======= + }, + excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], + env: { + mkDocsBaseUrl: 'http://localhost:8000', + backstageBaseUrl: 'http://localhost:3000', + 'cypress-plugin-snapshots': { + autoCleanUp: false, + imageConfig: { + resizeDevicePixelRatio: true, + threshold: 0.01, + }, + }, + }, +>>>>>>> 533a1a2db2 (update to cypress 10) viewportWidth: 1920, viewportHeight: 1080, includeShadowDom: true, diff --git a/packages/techdocs-cli/cypress/plugins/index.js b/packages/techdocs-cli/cypress/plugins/index.js new file mode 100644 index 0000000000..61af7e7f9b --- /dev/null +++ b/packages/techdocs-cli/cypress/plugins/index.js @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +require('@frsource/cypress-plugin-visual-regression-diff/plugin'); diff --git a/yarn.lock b/yarn.lock index 5b268ef6fd..7844d0fadb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,3 +1,6 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + __metadata: version: 6 cacheKey: 8 @@ -13398,7 +13401,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-techdocs-node": "workspace:^" - "@frsource/cypress-plugin-visual-regression-diff": ^3.2.4 + "@frsource/cypress-plugin-visual-regression-diff": ^3.2.8 "@types/commander": ^2.12.2 "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 From f188cc4108defacbfc0ac061de0470ca8b84df16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Wed, 7 Dec 2022 14:45:10 +0100 Subject: [PATCH 045/130] add changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- .changeset/cool-ladybugs-argue.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/cool-ladybugs-argue.md diff --git a/.changeset/cool-ladybugs-argue.md b/.changeset/cool-ladybugs-argue.md new file mode 100644 index 0000000000..fe948a8c74 --- /dev/null +++ b/.changeset/cool-ladybugs-argue.md @@ -0,0 +1,8 @@ +--- +'@backstage/core-components': minor +'@backstage/create-app': minor +'@techdocs/cli': minor +'@backstage/plugin-dynatrace': minor +--- + +Change from cypress-plugin-snapshots to cypress-plugin-visual-regression-diff and update to cypress 10 From fbfda48dffe1eb1cd51e378dc7adee0a59c851a7 Mon Sep 17 00:00:00 2001 From: malin-mallan <78034678+malin-mallan@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:00:42 +0100 Subject: [PATCH 046/130] Delete cool-ladybugs-argue.md Signed-off-by: malin-mallan <78034678+malin-mallan@users.noreply.github.com> --- .changeset/cool-ladybugs-argue.md | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .changeset/cool-ladybugs-argue.md diff --git a/.changeset/cool-ladybugs-argue.md b/.changeset/cool-ladybugs-argue.md deleted file mode 100644 index fe948a8c74..0000000000 --- a/.changeset/cool-ladybugs-argue.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': minor -'@backstage/create-app': minor -'@techdocs/cli': minor -'@backstage/plugin-dynatrace': minor ---- - -Change from cypress-plugin-snapshots to cypress-plugin-visual-regression-diff and update to cypress 10 From ea7caa40b9ac3592c7a93279b2b336d0c7a81bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Wed, 21 Dec 2022 15:02:05 +0100 Subject: [PATCH 047/130] solve merge conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- packages/techdocs-cli/cypress.config.js | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/techdocs-cli/cypress.config.js b/packages/techdocs-cli/cypress.config.js index b33ab2eace..abaa324d76 100644 --- a/packages/techdocs-cli/cypress.config.js +++ b/packages/techdocs-cli/cypress.config.js @@ -21,25 +21,9 @@ module.exports = defineConfig({ setupNodeEvents(on, config) { initPlugin(on, config); }, -<<<<<<< HEAD + excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], }, - -======= - }, - excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], - env: { - mkDocsBaseUrl: 'http://localhost:8000', - backstageBaseUrl: 'http://localhost:3000', - 'cypress-plugin-snapshots': { - autoCleanUp: false, - imageConfig: { - resizeDevicePixelRatio: true, - threshold: 0.01, - }, - }, - }, ->>>>>>> 533a1a2db2 (update to cypress 10) viewportWidth: 1920, viewportHeight: 1080, includeShadowDom: true, From 266e5eb9e31094109aed21bc2104bbc12828e158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MALIN=20WID=C3=88N?= Date: Tue, 20 Dec 2022 16:58:54 +0100 Subject: [PATCH 048/130] change folder structure in techdocs-cli/cypress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MALIN WIDÈN --- .../techdocs-cli/cypress/plugins/index.js | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 packages/techdocs-cli/cypress/plugins/index.js diff --git a/packages/techdocs-cli/cypress/plugins/index.js b/packages/techdocs-cli/cypress/plugins/index.js deleted file mode 100644 index 61af7e7f9b..0000000000 --- a/packages/techdocs-cli/cypress/plugins/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -require('@frsource/cypress-plugin-visual-regression-diff/plugin'); From 9060ad0ab9b249d2dae59a71cf87795d003ef72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 31 Jan 2023 10:30:37 +0100 Subject: [PATCH 049/130] dedupe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7844d0fadb..e5d7307f12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30103,7 +30103,7 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^4.0.1, npmlog@npm:^4.1.2": +"npmlog@npm:^4.1.2": version: 4.1.2 resolution: "npmlog@npm:4.1.2" dependencies: @@ -32026,30 +32026,7 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^7.1.0": - version: 7.1.0 - resolution: "prebuild-install@npm:7.1.0" - dependencies: - detect-libc: ^2.0.0 - expand-template: ^2.0.3 - github-from-package: 0.0.0 - minimist: ^1.2.3 - mkdirp-classic: ^0.5.3 - napi-build-utils: ^1.0.1 - node-abi: ^3.3.0 - npmlog: ^4.0.1 - pump: ^3.0.0 - rc: ^1.2.7 - simple-get: ^4.0.0 - tar-fs: ^2.0.0 - tunnel-agent: ^0.6.0 - bin: - prebuild-install: bin.js - checksum: 204f2d89c6d6179fa1039036514aa72f7d0b537e421ef72c40840286e318f41489f00f22c6acc725cce6e10d43825b69dcabeaadfc917db781c58cd56fc25f90 - languageName: node - linkType: hard - -"prebuild-install@npm:^7.1.1": +"prebuild-install@npm:^7.1.0, prebuild-install@npm:^7.1.1": version: 7.1.1 resolution: "prebuild-install@npm:7.1.1" dependencies: From c578ab658393caf2c0f9bdaf686efd036925b9b1 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 3 Feb 2023 10:34:35 +0000 Subject: [PATCH 050/130] docs: adjust code block type in permission tutorial step 1 Signed-off-by: MT Lewis --- docs/permissions/plugin-authors/01-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/plugin-authors/01-setup.md b/docs/permissions/plugin-authors/01-setup.md index 04e31b859e..a38b81e679 100644 --- a/docs/permissions/plugin-authors/01-setup.md +++ b/docs/permissions/plugin-authors/01-setup.md @@ -48,7 +48,7 @@ The source code is available here: Create a new `packages/backend/src/plugins/todolist.ts` with the following content: - ```javascript + ```typescript import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; import { createRouter } from '@internal/plugin-todo-list-backend'; import { Router } from 'express'; From 8d6d1fb789ba3a6a8982df2083c017dd6583a8ec Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 3 Feb 2023 10:34:53 +0000 Subject: [PATCH 051/130] docs: miscellaneous permission tutorial step 2 fixes Signed-off-by: MT Lewis --- .../02-adding-a-basic-permission-check.md | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 241b175987..551e48f91a 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -146,7 +146,7 @@ In order to test the logic above, the integrators of your backstage instance nee - async handle(): Promise { + async handle( + request: PolicyQuery, -+ user?: BackstageIdentityResponse, ++ _user?: BackstageIdentityResponse, + ): Promise { + if (isPermission(request.permission, todoListCreatePermission)) { + return { @@ -204,7 +204,7 @@ First we'll clean up the `plugins/todo-list-backend/src/service/router.test.ts`: const router = await createRouter({ logger: getVoidLogger(), identity: {} as DefaultIdentityClient, -+ permissions: toPermissionEvaluator, ++ permissions: permissionEvaluator, }); app = express().use(router); }); @@ -235,7 +235,7 @@ Then we want to update the `plugins/todo-list-backend/src/service/standaloneServ + ServerTokenManager, } from '@backstage/backend-common'; import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; - import { ServerPermissionClient } from '@backstage/plugin-permission-node'; ++ import { ServerPermissionClient } from '@backstage/plugin-permission-node'; import { Server } from 'http'; import { Logger } from 'winston'; import { createRouter } from './router'; @@ -285,4 +285,44 @@ Then we want to update the `plugins/todo-list-backend/src/service/standaloneServ module.hot?.accept(); ``` +Finally, we need to update `plugins/todo-list-backend/src/plugin.ts`: + +```diff + import { loggerToWinstonLogger } from '@backstage/backend-common'; + import { + coreServices, + createBackendPlugin, + } from '@backstage/backend-plugin-api'; + import { createRouter } from './service/router'; + + /** + * The example TODO list backend plugin. + * + * @alpha + */ + export const exampleTodoListPlugin = createBackendPlugin({ + id: 'exampleTodoList', + register(env) { + env.registerInit({ + deps: { + identity: coreServices.identity, + logger: coreServices.logger, + httpRouter: coreServices.httpRouter, ++ permissions: coreServices.permissions, + }, +- async init({ identity, logger, httpRouter }) { ++ async init({ identity, logger, httpRouter, permissions }) { + httpRouter.use( + await createRouter({ + identity, + logger: loggerToWinstonLogger(logger), + permissions, + }), + ); + }, + }); + }, + }); +``` + Now when you run `yarn tsc` you should have no more errors. From ce2c9ac363abbfcfcbb74eb8436b27c402e49766 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 3 Feb 2023 10:45:31 +0000 Subject: [PATCH 052/130] docs: miscellaneous permission tutorial step 3 fixes Signed-off-by: MT Lewis --- .../03-adding-a-resource-permission-check.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 8913b6d438..b063727c71 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -76,7 +76,7 @@ This enables decisions based on characteristics of the resource, but it's import Install the missing module: ``` -$ yarn workspace @internal/plugin-todo-list-backend add @backstage/plugin-permission-node +$ yarn workspace @internal/plugin-todo-list-backend add @backstage/plugin-permission-node zod ``` Create a new `plugins/todo-list-backend/src/service/rules.ts` file and append the following code: @@ -89,7 +89,8 @@ import { Todo, TodoFilter } from './todos'; export const createTodoListPermissionRule = makeCreatePermissionRule< Todo, - TodoFilter + TodoFilter, + typeof TODO_LIST_RESOURCE_TYPE >(); export const isOwner = createTodoListPermissionRule({ @@ -97,8 +98,8 @@ export const isOwner = createTodoListPermissionRule({ description: 'Should allow only if the todo belongs to the user', resourceType: TODO_LIST_RESOURCE_TYPE, paramsSchema: z.object({ - userId: z.string().describe('User ID to match on the resource') - }) + userId: z.string().describe('User ID to match on the resource'), + }), apply: (resource: Todo, { userId }) => { return resource.author === userId; }, @@ -187,6 +188,7 @@ Make sure `todoListConditions` and `createTodoListConditionalDecision` are expor ```diff export * from './service/router'; + export * from './conditionExports'; + export { exampleTodoListPlugin } from './plugin'; ``` ## Test the authorized update endpoint @@ -209,7 +211,6 @@ Let's go back to the permission policy's handle function and try to authorize ou + import { + todoListCreatePermission, + todoListUpdatePermission, -+ TODO_LIST_RESOURCE_TYPE, + } from '@internal/plugin-todo-list-common'; + import { + todoListConditions, @@ -217,7 +218,11 @@ Let's go back to the permission policy's handle function and try to authorize ou + } from '@internal/plugin-todo-list-backend'; ... - + async handle( + request: PolicyQuery, +- _user?: BackstageIdentityResponse, ++ user?: BackstageIdentityResponse, + ): Promise { if (isPermission(request.permission, todoListCreatePermission)) { return { result: AuthorizeResult.ALLOW, @@ -236,6 +241,7 @@ Let's go back to the permission policy's handle function and try to authorize ou return { result: AuthorizeResult.ALLOW, }; + } ``` For any incoming update requests, we now return a _Conditional Decision_. We are saying: From f3bf8fea2439e714734b186c6eeb3db7157f11af Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 3 Feb 2023 11:00:56 +0000 Subject: [PATCH 053/130] docs: miscellaneous permission tutorial step 4 fixes Signed-off-by: MT Lewis --- .../04-authorizing-access-to-paginated-data.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md index 8ddcffdcd9..e84f53f176 100644 --- a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md @@ -15,7 +15,7 @@ One possible solution may leverage the batching functionality to authorize all o - res.json(getAll()) + const items = getAll(); + const decisions = await permissions.authorize( -+ items.map(({ id }) => ({ permission: todosListRead, resourceRef: id })), ++ items.map(({ id }) => ({ permission: todoListReadPermission, resourceRef: id })), + ); + const filteredItems = decisions.filter( @@ -53,7 +53,7 @@ Let's add another permission to the plugin. resourceType: TODO_LIST_RESOURCE_TYPE, }); + -+ export const todosListRead = createPermission({ ++ export const todoListReadPermission = createPermission({ + name: 'todos.list.read', + attributes: { action: 'read' }, + resourceType: TODO_LIST_RESOURCE_TYPE, @@ -81,10 +81,9 @@ So far we've only used the `PermissionEvaluator.authorize` method, which will ev import { todosListCreate, todosListUpdate, -+ todosListRead, ++ todoListReadPermission, TODO_LIST_RESOURCE_TYPE, } from './permissions'; -+ import { rules } from './rules'; + const transformConditions: ConditionTransformer = createConditionTransformer(Object.values(rules)); @@ -95,7 +94,7 @@ So far we've only used the `PermissionEvaluator.authorize` method, which will ev + ); + + const decision = ( -+ await permissions.authorizeConditional([{ permission: todosListRead }], { ++ await permissions.authorizeConditional([{ permission: todoListReadPermission }], { + token, + }) + )[0]; @@ -110,7 +109,6 @@ So far we've only used the `PermissionEvaluator.authorize` method, which will ev + } else { + res.json(getAll()); + } -+ } - res.json(getAll()); }); ``` @@ -121,7 +119,7 @@ Since `TodoFilter` used in our plugin matches the structure of the conditions ob ## Test the authorized read endpoint -Let's update our permission policy to return a conditional result whenever a `todosListRead` permission is received. In this case, we can reuse the decision returned for the `todosListCreate` permission. +Let's update our permission policy to return a conditional result whenever a `todoListReadPermission` permission is received. In this case, we can reuse the decision returned for the `todosListCreate` permission. ```diff // packages/backend/src/plugins/permission.ts @@ -132,7 +130,6 @@ import { todoListCreatePermission, todoListUpdatePermission, + todoListReadPermission, - TODO_LIST_RESOURCE_TYPE, } from '@internal/plugin-todo-list-common'; ... From 812ffcc292798cf5202e7f2d6afbcb5586528682 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 3 Feb 2023 12:55:50 +0000 Subject: [PATCH 054/130] docs: miscellaneous permission tutorial step 5 fixes Signed-off-by: MT Lewis --- .../05-frontend-authorization.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/permissions/plugin-authors/05-frontend-authorization.md b/docs/permissions/plugin-authors/05-frontend-authorization.md index d494ea60ba..753ce56461 100644 --- a/docs/permissions/plugin-authors/05-frontend-authorization.md +++ b/docs/permissions/plugin-authors/05-frontend-authorization.md @@ -56,7 +56,11 @@ Let's make the following changes in `plugins/todo-list/src/components/TodoListPa - Add - + {!loadingPermission && ( -+ + )} @@ -118,7 +122,10 @@ Providing a disabled state can be a helpful signal to users, but there may be ca - - - -+ }> ++ } ++ > + + + @@ -149,11 +156,15 @@ Providing a disabled state can be a helpful signal to users, but there may be ca onChange={e => (title.current = e.target.value)} /> - {!loadingPermission && ( -- - )} -+ From 27226d88a992b0c88dc7fe1767f931d03178f738 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 3 Feb 2023 14:21:57 +0100 Subject: [PATCH 055/130] chore: fixing api-reports Signed-off-by: blam --- plugins/badges-backend/api-report.md | 2 +- plugins/bitbucket-cloud-common/api-report.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/badges-backend/api-report.md b/plugins/badges-backend/api-report.md index 0ecf41ee85..8ca4b561a2 100644 --- a/plugins/badges-backend/api-report.md +++ b/plugins/badges-backend/api-report.md @@ -79,7 +79,7 @@ export type BadgeSpec = { }; // @public (undocumented) -export type BadgeStyle = typeof BADGE_STYLES[number]; +export type BadgeStyle = (typeof BADGE_STYLES)[number]; // @public (undocumented) export const createDefaultBadgeFactories: () => BadgeFactories; diff --git a/plugins/bitbucket-cloud-common/api-report.md b/plugins/bitbucket-cloud-common/api-report.md index 5182579a4b..95e6f36836 100644 --- a/plugins/bitbucket-cloud-common/api-report.md +++ b/plugins/bitbucket-cloud-common/api-report.md @@ -132,7 +132,7 @@ export namespace Models { readonly Plaintext: 'plaintext'; }; export type BaseCommitSummaryMarkupEnum = - typeof BaseCommitSummaryMarkupEnum[keyof typeof BaseCommitSummaryMarkupEnum]; + (typeof BaseCommitSummaryMarkupEnum)[keyof typeof BaseCommitSummaryMarkupEnum]; export interface Branch { default_merge_strategy?: string; // (undocumented) @@ -150,7 +150,7 @@ export namespace Models { readonly FastForward: 'fast_forward'; }; export type BranchMergeStrategiesEnum = - typeof BranchMergeStrategiesEnum[keyof typeof BranchMergeStrategiesEnum]; + (typeof BranchMergeStrategiesEnum)[keyof typeof BranchMergeStrategiesEnum]; export interface Commit extends BaseCommit { // (undocumented) participants?: Array; @@ -179,7 +179,7 @@ export namespace Models { }; // (undocumented) export type CommitFileAttributesEnum = - typeof CommitFileAttributesEnum[keyof typeof CommitFileAttributesEnum]; + (typeof CommitFileAttributesEnum)[keyof typeof CommitFileAttributesEnum]; export interface Link { // (undocumented) href?: string; @@ -221,7 +221,7 @@ export namespace Models { }; // (undocumented) export type ParticipantRoleEnum = - typeof ParticipantRoleEnum[keyof typeof ParticipantRoleEnum]; + (typeof ParticipantRoleEnum)[keyof typeof ParticipantRoleEnum]; const // (undocumented) ParticipantStateEnum: { readonly Approved: 'approved'; @@ -230,7 +230,7 @@ export namespace Models { }; // (undocumented) export type ParticipantStateEnum = - typeof ParticipantStateEnum[keyof typeof ParticipantStateEnum]; + (typeof ParticipantStateEnum)[keyof typeof ParticipantStateEnum]; export interface Project extends ModelObject { // (undocumented) created_on?: string; @@ -306,7 +306,7 @@ export namespace Models { readonly NoForks: 'no_forks'; }; export type RepositoryForkPolicyEnum = - typeof RepositoryForkPolicyEnum[keyof typeof RepositoryForkPolicyEnum]; + (typeof RepositoryForkPolicyEnum)[keyof typeof RepositoryForkPolicyEnum]; const // (undocumented) RepositoryScmEnum: { readonly Git: 'git'; @@ -336,7 +336,7 @@ export namespace Models { } // (undocumented) export type RepositoryScmEnum = - typeof RepositoryScmEnum[keyof typeof RepositoryScmEnum]; + (typeof RepositoryScmEnum)[keyof typeof RepositoryScmEnum]; // (undocumented) export interface SearchCodeSearchResult { // (undocumented) From 8f6b71ae39ac5d1ade7a4abe90a4737105f105e2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:27:32 +0000 Subject: [PATCH 056/130] Update dependency @graphql-tools/schema to v9.0.15 Signed-off-by: Renovate Bot --- yarn.lock | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3d809a40c8..f042b70bf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10070,7 +10070,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:8.3.16, @graphql-tools/merge@npm:^8.2.6": +"@graphql-tools/merge@npm:8.3.16": version: 8.3.16 resolution: "@graphql-tools/merge@npm:8.3.16" dependencies: @@ -10082,6 +10082,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:8.3.17, @graphql-tools/merge@npm:^8.2.6": + version: 8.3.17 + resolution: "@graphql-tools/merge@npm:8.3.17" + dependencies: + "@graphql-tools/utils": 9.2.0 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 943e1ad64575ab9ae6726d434cf7b08e80099442f9d25c1ac9f11fdac7bd0e8e73109f0e2469b47650ad0a0731ac850ede844b5ff8a7eab352027a42c1e78c79 + languageName: node + linkType: hard + "@graphql-tools/mock@npm:^8.1.2": version: 8.6.8 resolution: "@graphql-tools/mock@npm:8.6.8" @@ -10191,7 +10203,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/schema@npm:9.0.14, @graphql-tools/schema@npm:^9.0.0": +"@graphql-tools/schema@npm:9.0.14": version: 9.0.14 resolution: "@graphql-tools/schema@npm:9.0.14" dependencies: @@ -10205,6 +10217,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/schema@npm:^9.0.0": + version: 9.0.15 + resolution: "@graphql-tools/schema@npm:9.0.15" + dependencies: + "@graphql-tools/merge": 8.3.17 + "@graphql-tools/utils": 9.2.0 + tslib: ^2.4.0 + value-or-promise: 1.0.12 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 4361cb1a355e8811fd9b762bd137a2f8fd287b05f03cb574bc3d5e6855aeceef462e72bacfa89dd9028138e9fe3cafc4d69c95d7f9a9b6ab7f037ca3e36f52a9 + languageName: node + linkType: hard + "@graphql-tools/url-loader@npm:7.16.28, @graphql-tools/url-loader@npm:^7.13.2, @graphql-tools/url-loader@npm:^7.9.7": version: 7.16.28 resolution: "@graphql-tools/url-loader@npm:7.16.28" @@ -10283,7 +10309,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:9.1.4, @graphql-tools/utils@npm:^9.0.0, @graphql-tools/utils@npm:^9.1.1": +"@graphql-tools/utils@npm:9.1.4": version: 9.1.4 resolution: "@graphql-tools/utils@npm:9.1.4" dependencies: @@ -10294,6 +10320,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/utils@npm:9.2.0, @graphql-tools/utils@npm:^9.0.0, @graphql-tools/utils@npm:^9.1.1": + version: 9.2.0 + resolution: "@graphql-tools/utils@npm:9.2.0" + dependencies: + "@graphql-typed-document-node/core": ^3.1.1 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 9754f8c6d52490df395626cef224542f37ca040ced6688c8ad80b1e0498006186529a50ac21979465123debba093db33dbfe8f1f7a9101f969dec0f31dc9a45b + languageName: node + linkType: hard + "@graphql-tools/wrap@npm:9.2.23": version: 9.2.23 resolution: "@graphql-tools/wrap@npm:9.2.23" From 179d3015184ce0b0a2d9b9b1abc7a4f422256943 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Jan 2023 22:55:41 +0100 Subject: [PATCH 057/130] added eslint-plugin Signed-off-by: Patrik Oldsberg --- .changeset/tough-jeans-camp.md | 5 + .github/vale/Vocab/Backstage/accept.txt | 3 +- packages/eslint-plugin/README.md | 40 ++++++ packages/eslint-plugin/index.js | 29 ++++ packages/eslint-plugin/package.json | 16 +++ .../rules/no-forbidden-package-imports.js | 124 ++++++++++++++++++ yarn.lock | 6 + 7 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 .changeset/tough-jeans-camp.md create mode 100644 packages/eslint-plugin/README.md create mode 100644 packages/eslint-plugin/index.js create mode 100644 packages/eslint-plugin/package.json create mode 100644 packages/eslint-plugin/rules/no-forbidden-package-imports.js diff --git a/.changeset/tough-jeans-camp.md b/.changeset/tough-jeans-camp.md new file mode 100644 index 0000000000..b7c08593e1 --- /dev/null +++ b/.changeset/tough-jeans-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/eslint-plugin': minor +--- + +Added a new ESLint plugin with common rules for Backstage projects. diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index f24b701d36..9e7cf7aa98 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -336,6 +336,7 @@ subfolders subheader subheaders subkey +subpath subroutes subtree superfences @@ -418,4 +419,4 @@ Dominik Henneke Kuang Frontside -Kaswell \ No newline at end of file +Kaswell diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md new file mode 100644 index 0000000000..a063d801d3 --- /dev/null +++ b/packages/eslint-plugin/README.md @@ -0,0 +1,40 @@ +# @backstage/eslint-plugin + +A collection of ESLint rules useful to Backstage projects. + +## Usage + +This ESLint plugin is part of the default lint configuration provided by the [Backstage CLI](https://www.npmjs.com/package/@backstage/cli), so you generally do not need to install it manually. + +If you do wish to install this plugin manually, start by adding it as a development dependency to your project: + +```sh +yarn add --dev @backstage/eslint-plugin +``` + +Then add it to your ESLint configuration: + +```js +extends: [ + 'plugin:@backstage/recommended', +], +``` + +Alternatively, if you want to install in individual rules manually: + +```js +plugins: [ + '@backstage', +], +rules: { + '@backstage/no-forbidden-package-imports': 'error', +} +``` + +## Rules + +The following rules are provided by this plugin: + +| Rule | Description | +| ----------------------------------------- | ------------------------------------------------------------------------------- | +| `@backstage/no-forbidden-package-imports` | Disallow internal monorepo imports from package subpaths that are not exported. | diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js new file mode 100644 index 0000000000..11d17b2b8f --- /dev/null +++ b/packages/eslint-plugin/index.js @@ -0,0 +1,29 @@ +/* + * Copyright 2023 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. + */ + +module.exports = { + configs: { + recommended: { + plugins: ['@backstage'], + rules: { + '@backstage/no-forbidden-package-imports': 'error', + }, + }, + }, + rules: { + 'no-forbidden-package-imports': require('./rules/no-forbidden-package-imports'), + }, +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json new file mode 100644 index 0000000000..9724cec32b --- /dev/null +++ b/packages/eslint-plugin/package.json @@ -0,0 +1,16 @@ +{ + "name": "@backstage/eslint-plugin", + "description": "Backstage ESLint plugin", + "version": "0.0.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/eslint-plugin" + }, + "license": "Apache-2.0", + "main": "./index.js" +} diff --git a/packages/eslint-plugin/rules/no-forbidden-package-imports.js b/packages/eslint-plugin/rules/no-forbidden-package-imports.js new file mode 100644 index 0000000000..d5cce80368 --- /dev/null +++ b/packages/eslint-plugin/rules/no-forbidden-package-imports.js @@ -0,0 +1,124 @@ +'use strict'; +/* + * Copyright 2023 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. + */ + +// @ts-check + +// Note: this file is ES5, because we want to avoid having to transpile it for local development + +var fs = require('fs'); +var path = require('path'); +/** @type {import('@manypkg/get-packages')} */ +var manypkg = require('@manypkg/get-packages'); + +// Loads all packages in the monorepo once, and caches the result +var getPackageMap = (function () { + var map = undefined; + var lastLoadAt = undefined; + + /** @returns {Record} */ + return function (/** @type {string} */ dir) { + if (map) { + // Only cache for 5 seconds, to avoid the need to reload ESLint servers + if (Date.now() - lastLoadAt > 5000) { + map = undefined; + } else { + return map; + } + } + var packages = manypkg.getPackagesSync(dir).packages; + if (!packages) { + return undefined; + } + map = {}; + lastLoadAt = Date.now(); + packages.forEach(function (pkg) { + map[pkg.packageJson.name] = pkg; + }); + return map; + }; +})(); + +module.exports = { + meta: { + type: 'problem', + messages: { + forbidden: '{{packageName}} does not export {{subPath}}', + }, + }, + create: function (/** @type import('eslint').Rule.RuleContext */ context) { + var packageMap = getPackageMap(context.getCwd()); + if (!packageMap) { + return; + } + + function checkImport(node) { + if (!node.source) { + return; + } + var importPath = node.source.value; + if (importPath[0] === '.') { + return; + } + + var pathParts = importPath.split('/'); + + // Check for match with plain name, then namespaced name + var name = pathParts.shift(); + if (!packageMap[name]) { + name += '/' + pathParts.shift(); + } + if (!packageMap[name]) { + return; + } + + // Empty subpaths are always allowed + var subPath = pathParts.join('/'); + if (!subPath) { + return; + } + + var pkg = packageMap[name]; + + // If the import is listed in the package.json exports field, we allow it + var exp = pkg.packageJson.exports; + if (exp && (exp[subPath] || exp['./' + subPath])) { + return; + } + + // If there's a package.json in the target directory, we allow the import + if (fs.existsSync(path.resolve(pkg.dir, subPath, 'package.json'))) { + return; + } + + context.report({ + node: node, + messageId: 'forbidden', + data: { + packageName: pkg.packageJson.name || '', + subPath: subPath, + }, + }); + } + + return { + ImportDeclaration: checkImport, + ExportAllDeclaration: checkImport, + ExportNamedDeclaration: checkImport, + ImportExpression: checkImport, + }; + }, +}; diff --git a/yarn.lock b/yarn.lock index 3d809a40c8..ac5c6d0541 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4095,6 +4095,12 @@ __metadata: languageName: unknown linkType: soft +"@backstage/eslint-plugin@workspace:packages/eslint-plugin": + version: 0.0.0-use.local + resolution: "@backstage/eslint-plugin@workspace:packages/eslint-plugin" + languageName: unknown + linkType: soft + "@backstage/integration-aws-node@workspace:^, @backstage/integration-aws-node@workspace:packages/integration-aws-node": version: 0.0.0-use.local resolution: "@backstage/integration-aws-node@workspace:packages/integration-aws-node" From 98b2bd1b51688ae5947a755dc13e023a6fa5e739 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 18 Jan 2023 21:53:55 +0100 Subject: [PATCH 058/130] eslint-plugin: added missing deps + lint config Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/.eslintrc.js | 35 +++++++++++++++++++++++++++++ packages/eslint-plugin/package.json | 11 ++++++++- yarn.lock | 3 +++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin/.eslintrc.js diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js new file mode 100644 index 0000000000..aaf907cfbc --- /dev/null +++ b/packages/eslint-plugin/.eslintrc.js @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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. + */ + +// Custom lint config here since source is ES5 +module.exports = { + plugins: ['import'], + parserOptions: { + ecmaVersion: 5, + }, + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: false, + optionalDependencies: true, + peerDependencies: true, + bundledDependencies: true, + }, + ], + 'no-unused-expressions': 'off', + }, +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 9724cec32b..ee5e62c41e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -12,5 +12,14 @@ "directory": "packages/eslint-plugin" }, "license": "Apache-2.0", - "main": "./index.js" + "main": "./index.js", + "scripts": { + "lint": "backstage-cli package lint" + }, + "dependencies": { + "@manypkg/get-packages": "^1.1.3" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + } } diff --git a/yarn.lock b/yarn.lock index ac5c6d0541..06cd22d84a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4098,6 +4098,9 @@ __metadata: "@backstage/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@backstage/eslint-plugin@workspace:packages/eslint-plugin" + dependencies: + "@backstage/cli": "workspace:^" + "@manypkg/get-packages": ^1.1.3 languageName: unknown linkType: soft From 4d05d5bf8362ac5e86c6e75918aa7cdbb14a3a79 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 11:10:38 +0100 Subject: [PATCH 059/130] eslint-plugin: refactor to ES2021 and add no-undeclared-imports rule Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/.eslintrc.js | 7 +- packages/eslint-plugin/index.js | 2 + packages/eslint-plugin/lib/getPackages.js | 63 +++++++ packages/eslint-plugin/lib/visitImports.js | 100 +++++++++++ packages/eslint-plugin/package.json | 3 +- .../rules/no-forbidden-package-imports.js | 91 ++-------- .../rules/no-undeclared-imports.js | 156 ++++++++++++++++++ yarn.lock | 1 + 8 files changed, 339 insertions(+), 84 deletions(-) create mode 100644 packages/eslint-plugin/lib/getPackages.js create mode 100644 packages/eslint-plugin/lib/visitImports.js create mode 100644 packages/eslint-plugin/rules/no-undeclared-imports.js diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js index aaf907cfbc..74f93a6475 100644 --- a/packages/eslint-plugin/.eslintrc.js +++ b/packages/eslint-plugin/.eslintrc.js @@ -17,8 +17,9 @@ // Custom lint config here since source is ES5 module.exports = { plugins: ['import'], - parserOptions: { - ecmaVersion: 5, + env: { + node: true, + es2021: true, }, rules: { 'import/no-extraneous-dependencies': [ @@ -30,6 +31,6 @@ module.exports = { bundledDependencies: true, }, ], - 'no-unused-expressions': 'off', + // 'no-unused-expressions': 'off', }, }; diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index 11d17b2b8f..cf1dd20ab7 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -20,10 +20,12 @@ module.exports = { plugins: ['@backstage'], rules: { '@backstage/no-forbidden-package-imports': 'error', + '@backstage/no-undeclared-imports': 'error', }, }, }, rules: { 'no-forbidden-package-imports': require('./rules/no-forbidden-package-imports'), + 'no-undeclared-imports': require('./rules/no-undeclared-imports'), }, }; diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js new file mode 100644 index 0000000000..91043fe0d5 --- /dev/null +++ b/packages/eslint-plugin/lib/getPackages.js @@ -0,0 +1,63 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +/** @type {import('@manypkg/get-packages')} */ +const manypkg = require('@manypkg/get-packages'); + +/** + * @typedef ExtendedPackage + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record }}} packageJson + */ + +/** + * @typedef PackageMap + * @type object + * + * @property {ExtendedPackage} root + * @property {ExtendedPackage[]} list + * @property {Map} map + */ + +// Loads all packages in the monorepo once, and caches the result +module.exports = (function () { + let result = undefined; + let lastLoadAt = undefined; + + /** @returns {PackageMap | undefined} */ + return function getPackages(/** @type {string} */ dir) { + if (result) { + // Only cache for 5 seconds, to avoid the need to reload ESLint servers + if (Date.now() - lastLoadAt > 5000) { + result = undefined; + } else { + return result; + } + } + const packages = manypkg.getPackagesSync(dir); + if (!packages) { + return undefined; + } + result = { + map: new Map(packages.packages.map(pkg => [pkg.packageJson.name, pkg])), + list: packages.packages, + root: packages.root, + }; + lastLoadAt = Date.now(); + return result; + }; +})(); diff --git a/packages/eslint-plugin/lib/visitImports.js b/packages/eslint-plugin/lib/visitImports.js new file mode 100644 index 0000000000..d73c3203a8 --- /dev/null +++ b/packages/eslint-plugin/lib/visitImports.js @@ -0,0 +1,100 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +const getPackages = require('./getPackages'); + +/** + * @typedef LocalImport + * @type {object} + * @property {'local'} type + * @property {string} path + */ + +/** + * @typedef InternalImport + * @type {object} + * @property {'internal'} type + * @property {string} path + * @property {import('./getPackages').ExtendedPackage} package + */ + +/** + * @typedef ExternalImport + * @type {object} + * @property {'external'} type + * @property {string} path + * @property {string} packageName + */ + +/** + * @callback ImportVisitor + * @param {import('eslint').Rule.Node} node + * @param {LocalImport | InternalImport | ExternalImport} import + */ + +/** + * @param visitor - Visitor callback + * @param {import('eslint').Rule.RuleContext} context + * @param {ImportVisitor} visitor + */ +module.exports = function visitImports(context, visitor) { + const packages = getPackages(context.getCwd()); + if (!packages) { + return; + } + + function visit(node) { + if (!node.source) { + return; + } + const importPath = node.source.value; + if (importPath[0] === '.') { + return visitor(node, { type: 'local', path: importPath }); + } + + const pathParts = importPath.split('/'); + + // Check for match with plain name, then namespaced name + let packageName; + let subPath; + if (importPath[0] === '@') { + packageName = pathParts.slice(0, 2).join('/'); + subPath = pathParts.slice(2).join('/'); + } else { + packageName = pathParts[0]; + subPath = pathParts.slice(1).join('/'); + } + const pkg = packages?.map.get(packageName); + if (!pkg) { + return visitor(node, { + type: 'external', + path: subPath, + packageName: packageName, + }); + } + + return visitor(node, { type: 'internal', path: subPath, package: pkg }); + } + + return { + ImportDeclaration: visit, + ExportAllDeclaration: visit, + ExportNamedDeclaration: visit, + ImportExpression: visit, + }; +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index ee5e62c41e..73862d2295 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -17,7 +17,8 @@ "lint": "backstage-cli package lint" }, "dependencies": { - "@manypkg/get-packages": "^1.1.3" + "@manypkg/get-packages": "^1.1.3", + "minimatch": "^5.1.2" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/packages/eslint-plugin/rules/no-forbidden-package-imports.js b/packages/eslint-plugin/rules/no-forbidden-package-imports.js index d5cce80368..bbf342dba0 100644 --- a/packages/eslint-plugin/rules/no-forbidden-package-imports.js +++ b/packages/eslint-plugin/rules/no-forbidden-package-imports.js @@ -1,4 +1,3 @@ -'use strict'; /* * Copyright 2023 The Backstage Authors * @@ -17,41 +16,9 @@ // @ts-check -// Note: this file is ES5, because we want to avoid having to transpile it for local development - -var fs = require('fs'); -var path = require('path'); -/** @type {import('@manypkg/get-packages')} */ -var manypkg = require('@manypkg/get-packages'); - -// Loads all packages in the monorepo once, and caches the result -var getPackageMap = (function () { - var map = undefined; - var lastLoadAt = undefined; - - /** @returns {Record} */ - return function (/** @type {string} */ dir) { - if (map) { - // Only cache for 5 seconds, to avoid the need to reload ESLint servers - if (Date.now() - lastLoadAt > 5000) { - map = undefined; - } else { - return map; - } - } - var packages = manypkg.getPackagesSync(dir).packages; - if (!packages) { - return undefined; - } - map = {}; - lastLoadAt = Date.now(); - packages.forEach(function (pkg) { - map[pkg.packageJson.name] = pkg; - }); - return map; - }; -})(); +const visitImports = require('../lib/visitImports'); +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'problem', @@ -59,48 +26,19 @@ module.exports = { forbidden: '{{packageName}} does not export {{subPath}}', }, }, - create: function (/** @type import('eslint').Rule.RuleContext */ context) { - var packageMap = getPackageMap(context.getCwd()); - if (!packageMap) { - return; - } - - function checkImport(node) { - if (!node.source) { + create(context) { + return visitImports(context, (node, imp) => { + if (imp.type !== 'internal') { return; } - var importPath = node.source.value; - if (importPath[0] === '.') { - return; - } - - var pathParts = importPath.split('/'); - - // Check for match with plain name, then namespaced name - var name = pathParts.shift(); - if (!packageMap[name]) { - name += '/' + pathParts.shift(); - } - if (!packageMap[name]) { - return; - } - // Empty subpaths are always allowed - var subPath = pathParts.join('/'); - if (!subPath) { + if (!imp.path) { return; } - var pkg = packageMap[name]; - // If the import is listed in the package.json exports field, we allow it - var exp = pkg.packageJson.exports; - if (exp && (exp[subPath] || exp['./' + subPath])) { - return; - } - - // If there's a package.json in the target directory, we allow the import - if (fs.existsSync(path.resolve(pkg.dir, subPath, 'package.json'))) { + const exp = imp.package.packageJson.exports; + if (exp && (exp[imp.path] || exp['./' + imp.path])) { return; } @@ -108,17 +46,10 @@ module.exports = { node: node, messageId: 'forbidden', data: { - packageName: pkg.packageJson.name || '', - subPath: subPath, + packageName: imp.package.packageJson.name || imp.package.dir, + subPath: imp.path, }, }); - } - - return { - ImportDeclaration: checkImport, - ExportAllDeclaration: checkImport, - ExportNamedDeclaration: checkImport, - ImportExpression: checkImport, - }; + }); }, }; diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js new file mode 100644 index 0000000000..462b4b2e31 --- /dev/null +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -0,0 +1,156 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +const path = require('path'); +const getPackageMap = require('../lib/getPackages'); +const visitImports = require('../lib/visitImports'); +const minimatch = require('minimatch'); + +const depFields = { + dep: 'dependencies', + dev: 'devDependencies', + peer: 'peerDependencies', +}; + +const peerDeps = ['react', 'react-dom', 'react-router', 'react-router-dom']; +const devModulePatterns = [ + new minimatch.Minimatch('!src/**'), + new minimatch.Minimatch('src/**/*.test.*'), + new minimatch.Minimatch('src/**/*.stories.*'), + new minimatch.Minimatch('src/**/__testUtils__/**'), + new minimatch.Minimatch('src/**/__mocks__/**'), + new minimatch.Minimatch('src/setupTests.*'), +]; + +function getExpectedDepType( + /** @type {string} */ impPath, + /** @type {string} */ modulePath, +) { + if (peerDeps.includes(impPath)) { + return 'peer'; + } + for (const pattern of devModulePatterns) { + if (pattern.match(modulePath)) { + return 'dev'; + } + } + return 'dep'; +} + +/** + * + * @param {import('@manypkg/get-packages').Package['packageJson']} pkg + * @param {string} name + * @param {ReturnType} expectedType + * @returns {{oldDepsField?: string, depsField: string} | undefined} + */ +function findConflict(pkg, name, expectedType) { + const isDep = pkg.dependencies?.[name]; + const isDevDep = pkg.devDependencies?.[name]; + const isPeerDep = pkg.peerDependencies?.[name]; + const depsField = depFields[expectedType]; + + if (expectedType === 'dep' && !isDep) { + const oldDepsField = isDevDep ? depFields.dev : undefined; + return { oldDepsField, depsField }; + } else if (expectedType === 'dev' && !isDevDep && !isDep && !isPeerDep) { + return { oldDepsField: undefined, depsField }; + } else if (expectedType === 'peer' && !isPeerDep) { + const oldDepsField = isDep + ? depFields.dep + : isDevDep + ? depFields.dev + : undefined; + + return { oldDepsField, depsField }; + } +} + +function getAddFlagForConflict(conflict) { + switch (conflict?.depsField) { + case depFields.dep: + return ''; + case depFields.dev: + return ' --dev'; + case depFields.peer: + return ' --peer'; + default: + return ''; + } +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + messages: { + undeclared: + "{{ packageName }} must be declared in {{ depsField }} of {{ packageJsonPath }}, run 'yarn --cwd {{ packagePath }} add{{ addFlag }} {{ packageName }}' from the project root.", + switch: + '{{ packageName }} is declared in {{ oldDepsField }}, but should be moved to {{ depsField }} in {{ packageJsonPath }}.', + }, + }, + create(context) { + const packages = getPackageMap(context.getCwd()); + if (!packages) { + return {}; + } + const filePath = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(); + + const localPkg = packages.list.find(p => + filePath.startsWith(p.dir + path.sep), + ); + if (!localPkg) { + return {}; + } + + return visitImports(context, (node, imp) => { + if (imp.type !== 'external') { + return; + } + + const modulePath = path.relative(localPkg.dir, filePath); + const expectedType = getExpectedDepType(imp.packageName, modulePath); + + const conflict = findConflict( + localPkg.packageJson, + imp.packageName, + expectedType, + ); + + if (conflict) { + const packagePath = path.relative(packages.root.dir, localPkg.dir); + const packageJsonPath = path.join(packagePath, 'package.json'); + + context.report({ + node, + messageId: conflict.oldDepsField ? 'switch' : 'undeclared', + data: { + ...conflict, + packagePath, + addFlag: getAddFlagForConflict(conflict), + packageName: imp.packageName, + packageJsonPath: packageJsonPath, + }, + }); + } + }); + }, +}; diff --git a/yarn.lock b/yarn.lock index 06cd22d84a..e0e1da349c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4101,6 +4101,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@manypkg/get-packages": ^1.1.3 + minimatch: ^5.1.2 languageName: unknown linkType: soft From f459b168f03033ca6c4025ce16941d7bc0480835 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 14:19:19 +0100 Subject: [PATCH 060/130] eslint-plugin: add support for detecting Node.js builtins Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/lib/visitImports.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/lib/visitImports.js b/packages/eslint-plugin/lib/visitImports.js index d73c3203a8..c89d65fb49 100644 --- a/packages/eslint-plugin/lib/visitImports.js +++ b/packages/eslint-plugin/lib/visitImports.js @@ -16,6 +16,7 @@ // @ts-check +const { builtinModules } = require('module'); const getPackages = require('./getPackages'); /** @@ -41,10 +42,18 @@ const getPackages = require('./getPackages'); * @property {string} packageName */ +/** + * @typedef BuiltinImport + * @type {object} + * @property {'builtin'} type + * @property {string} path + * @property {string} packageName + */ + /** * @callback ImportVisitor * @param {import('eslint').Rule.Node} node - * @param {LocalImport | InternalImport | ExternalImport} import + * @param {LocalImport | InternalImport | ExternalImport | BuiltinImport} import */ /** @@ -81,10 +90,17 @@ module.exports = function visitImports(context, visitor) { } const pkg = packages?.map.get(packageName); if (!pkg) { + if ( + packageName.startsWith('node:') || + builtinModules.includes(packageName) + ) { + return visitor(node, { type: 'builtin', path: subPath, packageName }); + } + return visitor(node, { type: 'external', path: subPath, - packageName: packageName, + packageName, }); } From 9e192d7f4a67e5a1c1ad35b2b6c27e2207b6c079 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 14:19:41 +0100 Subject: [PATCH 061/130] eslint-plugin: dogfood lint config Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/.eslintrc.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js index 74f93a6475..7fc68c3c5d 100644 --- a/packages/eslint-plugin/.eslintrc.js +++ b/packages/eslint-plugin/.eslintrc.js @@ -16,21 +16,13 @@ // Custom lint config here since source is ES5 module.exports = { - plugins: ['import'], + plugins: ['@backstage'], env: { node: true, es2021: true, }, rules: { - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: false, - optionalDependencies: true, - peerDependencies: true, - bundledDependencies: true, - }, - ], - // 'no-unused-expressions': 'off', + '@backstage/no-undeclared-imports': ['error'], + 'no-unused-expressions': 'off', }, }; From 8b4a758ed28e1d1268894f6d71d0847e365e286f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 14:38:40 +0100 Subject: [PATCH 062/130] eslint-plugin: add support for require calls Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/lib/visitImports.js | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/lib/visitImports.js b/packages/eslint-plugin/lib/visitImports.js index c89d65fb49..5184fb8d79 100644 --- a/packages/eslint-plugin/lib/visitImports.js +++ b/packages/eslint-plugin/lib/visitImports.js @@ -56,6 +56,36 @@ const getPackages = require('./getPackages'); * @param {LocalImport | InternalImport | ExternalImport | BuiltinImport} import */ +/** + * @param {import('estree').ImportDeclaration | import('estree').ExportAllDeclaration | import('estree').ExportNamedDeclaration | import('estree').ImportExpression | import('estree').SimpleCallExpression} node + * @returns {string | undefined} + */ +function getImportPath(node) { + /** @type {import('estree').Expression | import('estree').SpreadElement | undefined | null} */ + let pathNode; + + if (node.type === 'CallExpression') { + if ( + node.callee.type === 'Identifier' && + node.callee.name == 'require' && + node.arguments.length === 1 + ) { + pathNode = node.arguments[0]; + } + } else { + pathNode = node.source; + } + + if (pathNode?.type !== 'Literal') { + return undefined; + } + if (typeof pathNode.value !== 'string') { + return undefined; + } + + return pathNode.value; +} + /** * @param visitor - Visitor callback * @param {import('eslint').Rule.RuleContext} context @@ -68,10 +98,11 @@ module.exports = function visitImports(context, visitor) { } function visit(node) { - if (!node.source) { + const importPath = getImportPath(node); + if (!importPath) { return; } - const importPath = node.source.value; + if (importPath[0] === '.') { return visitor(node, { type: 'local', path: importPath }); } @@ -112,5 +143,6 @@ module.exports = function visitImports(context, visitor) { ExportAllDeclaration: visit, ExportNamedDeclaration: visit, ImportExpression: visit, + CallExpression: visit, }; }; From 765b7ee355ddcbb01c57942796a7cc57952c7839 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:14:07 +0100 Subject: [PATCH 063/130] eslint-plugin: backwards compatiblity check for packages without exports Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/lib/getPackages.js | 2 +- .../rules/no-forbidden-package-imports.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 91043fe0d5..318d0654c2 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages'); /** * @typedef ExtendedPackage - * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record }}} packageJson + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array }}} packageJson */ /** diff --git a/packages/eslint-plugin/rules/no-forbidden-package-imports.js b/packages/eslint-plugin/rules/no-forbidden-package-imports.js index bbf342dba0..6c861e17f5 100644 --- a/packages/eslint-plugin/rules/no-forbidden-package-imports.js +++ b/packages/eslint-plugin/rules/no-forbidden-package-imports.js @@ -41,6 +41,20 @@ module.exports = { if (exp && (exp[imp.path] || exp['./' + imp.path])) { return; } + if (!exp) { + // If there's no exports field, we allow anything listed in files, except dist + const files = imp.package.packageJson.files; + if ( + !files || + files.some(f => !f.startsWith('dist') && imp.path.startsWith(f)) + ) { + return; + } + // And also package.json + if (imp.path === 'package.json') { + return; + } + } context.report({ node: node, From d5eee2944cdc4ccb83f643ad22197c8064e28959 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:15:17 +0100 Subject: [PATCH 064/130] eslint-plugin: improved peer deps check and consider package role Signed-off-by: Patrik Oldsberg --- .../rules/no-undeclared-imports.js | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index 462b4b2e31..cec3503f8f 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -27,7 +27,6 @@ const depFields = { peer: 'peerDependencies', }; -const peerDeps = ['react', 'react-dom', 'react-router', 'react-router-dom']; const devModulePatterns = [ new minimatch.Minimatch('!src/**'), new minimatch.Minimatch('src/**/*.test.*'), @@ -38,12 +37,34 @@ const devModulePatterns = [ ]; function getExpectedDepType( + localPkg, /** @type {string} */ impPath, /** @type {string} */ modulePath, ) { - if (peerDeps.includes(impPath)) { - return 'peer'; + const role = localPkg?.backstage?.role; + // Some package roles have known dependency types + switch (role) { + case 'common-library': + case 'web-library': + case 'frontend-plugin': + case 'frontend-plugin-module': + case 'node-library': + case 'backend-plugin': + case 'backend-plugin-module': + switch (impPath) { + case 'react': + case 'react-dom': + case 'react-router': + case 'react-router-dom': + return 'peer'; + } + case 'cli': + case 'frontend': + case 'backend': + default: + break; } + for (const pattern of devModulePatterns) { if (pattern.match(modulePath)) { return 'dev'; @@ -65,7 +86,7 @@ function findConflict(pkg, name, expectedType) { const isPeerDep = pkg.peerDependencies?.[name]; const depsField = depFields[expectedType]; - if (expectedType === 'dep' && !isDep) { + if (expectedType === 'dep' && !isDep && !isPeerDep) { const oldDepsField = isDevDep ? depFields.dev : undefined; return { oldDepsField, depsField }; } else if (expectedType === 'dev' && !isDevDep && !isDep && !isPeerDep) { @@ -127,7 +148,11 @@ module.exports = { } const modulePath = path.relative(localPkg.dir, filePath); - const expectedType = getExpectedDepType(imp.packageName, modulePath); + const expectedType = getExpectedDepType( + localPkg, + imp.packageName, + modulePath, + ); const conflict = findConflict( localPkg.packageJson, From 58e481a4f9a4889d11720b18727d96eb2406b749 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:15:48 +0100 Subject: [PATCH 065/130] eslint-plugin: ignore type imports when checking deps Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/lib/visitImports.js | 39 +++++++++++++------ .../rules/no-undeclared-imports.js | 16 ++++++++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin/lib/visitImports.js b/packages/eslint-plugin/lib/visitImports.js index 5184fb8d79..ed16c26c7f 100644 --- a/packages/eslint-plugin/lib/visitImports.js +++ b/packages/eslint-plugin/lib/visitImports.js @@ -23,6 +23,7 @@ const getPackages = require('./getPackages'); * @typedef LocalImport * @type {object} * @property {'local'} type + * @property {'value' | 'type'} kind * @property {string} path */ @@ -30,6 +31,7 @@ const getPackages = require('./getPackages'); * @typedef InternalImport * @type {object} * @property {'internal'} type + * @property {'value' | 'type'} kind * @property {string} path * @property {import('./getPackages').ExtendedPackage} package */ @@ -38,6 +40,7 @@ const getPackages = require('./getPackages'); * @typedef ExternalImport * @type {object} * @property {'external'} type + * @property {'value' | 'type'} kind * @property {string} path * @property {string} packageName */ @@ -46,6 +49,7 @@ const getPackages = require('./getPackages'); * @typedef BuiltinImport * @type {object} * @property {'builtin'} type + * @property {'value' | 'type'} kind * @property {string} path * @property {string} packageName */ @@ -58,9 +62,9 @@ const getPackages = require('./getPackages'); /** * @param {import('estree').ImportDeclaration | import('estree').ExportAllDeclaration | import('estree').ExportNamedDeclaration | import('estree').ImportExpression | import('estree').SimpleCallExpression} node - * @returns {string | undefined} + * @returns {undefined | {path: string, kind: 'type' | 'value'}} */ -function getImportPath(node) { +function getImportInfo(node) { /** @type {import('estree').Expression | import('estree').SpreadElement | undefined | null} */ let pathNode; @@ -83,7 +87,9 @@ function getImportPath(node) { return undefined; } - return pathNode.value; + /** @type {any} */ + const anyNode = node; + return { path: pathNode.value, kind: anyNode.importKind ?? 'value' }; } /** @@ -98,21 +104,21 @@ module.exports = function visitImports(context, visitor) { } function visit(node) { - const importPath = getImportPath(node); - if (!importPath) { + const info = getImportInfo(node); + if (!info) { return; } - if (importPath[0] === '.') { - return visitor(node, { type: 'local', path: importPath }); + if (info.path[0] === '.') { + return visitor(node, { type: 'local', ...info }); } - const pathParts = importPath.split('/'); + const pathParts = info.path.split('/'); // Check for match with plain name, then namespaced name let packageName; let subPath; - if (importPath[0] === '@') { + if (info.path[0] === '@') { packageName = pathParts.slice(0, 2).join('/'); subPath = pathParts.slice(2).join('/'); } else { @@ -125,17 +131,28 @@ module.exports = function visitImports(context, visitor) { packageName.startsWith('node:') || builtinModules.includes(packageName) ) { - return visitor(node, { type: 'builtin', path: subPath, packageName }); + return visitor(node, { + type: 'builtin', + kind: info.kind, + path: subPath, + packageName, + }); } return visitor(node, { type: 'external', + kind: info.kind, path: subPath, packageName, }); } - return visitor(node, { type: 'internal', path: subPath, package: pkg }); + return visitor(node, { + type: 'internal', + kind: info.kind, + path: subPath, + package: pkg, + }); } return { diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index cec3503f8f..f7f7b17354 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -146,6 +146,10 @@ module.exports = { if (imp.type !== 'external') { return; } + // We leave checking of type imports to the repo-tools check + if (imp.kind === 'type') { + return; + } const modulePath = path.relative(localPkg.dir, filePath); const expectedType = getExpectedDepType( @@ -161,6 +165,18 @@ module.exports = { ); if (conflict) { + try { + const fullImport = imp.path + ? `${imp.packageName}/${imp.path}` + : imp.packageName; + require.resolve(fullImport, { + paths: [localPkg.dir], + }); + } catch { + // If the dependency doesn't resolve then it's likely a type import, ignore + return; + } + const packagePath = path.relative(packages.root.dir, localPkg.dir); const packageJsonPath = path.join(packagePath, 'package.json'); From 11df6f42b9c0b8ddb5408b1fb5b5196558f1c413 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:16:41 +0100 Subject: [PATCH 066/130] cli: use new eslint plugin + fix issues Signed-off-by: Patrik Oldsberg --- cypress/.eslintrc.js | 9 ------ packages/app/cypress/.eslintrc.json | 9 ------ .../backend-defaults/src/CreateBackend.ts | 2 +- packages/cli/asset-types/asset-types.d.ts | 2 +- packages/cli/config/eslint-factory.js | 31 ++----------------- .../lib/builder/buildTypeDefinitionsWorker.ts | 2 +- packages/cli/src/lib/version.test.ts | 1 - packages/codemods/.eslintrc.js | 15 +-------- packages/codemods/package.json | 2 +- .../LogViewer/RealLogViewer.test.tsx | 2 +- .../LogViewer/useLogViewerSelection.test.tsx | 2 +- packages/create-app/bin/backstage-create-app | 2 +- .../packages/app/cypress/.eslintrc.json | 9 ------ packages/e2e-test/.eslintrc.js | 15 +-------- packages/e2e-test/package.json | 16 +++++----- packages/e2e-test/src/commands/run.ts | 2 +- .../src/commands/type-deps/type-deps.ts | 2 +- .../cypress/.eslintrc.json | 9 ------ packages/techdocs-cli/cypress/.eslintrc.json | 9 ------ .../src/run.ts | 2 +- plugins/code-climate/dev/index.tsx | 2 +- plugins/code-climate/src/setupTests.ts | 2 +- .../example/templates/CostInsightsClient.ts | 2 +- plugins/example-todo-list/.eslintrc.js | 15 +-------- .../scripts/build-nunjucks.js | 2 +- .../src/components/TriggerDialog/testUtils.ts | 2 +- scripts/assemble-manifest.js | 2 +- scripts/check-docs-quality.js | 2 +- scripts/check-if-release.js | 2 +- scripts/create-github-release.js | 2 +- scripts/create-release-tag.js | 2 +- scripts/list-deprecations.js | 2 +- scripts/prepare-release.js | 2 +- scripts/snyk-github-issue-sync.ts | 2 +- scripts/verify-api-reference.js | 2 +- scripts/verify-changesets.js | 2 +- scripts/verify-links.js | 2 +- scripts/verify-lockfile-duplicates.js | 2 +- 38 files changed, 42 insertions(+), 150 deletions(-) diff --git a/cypress/.eslintrc.js b/cypress/.eslintrc.js index a77de4a629..07f264452d 100644 --- a/cypress/.eslintrc.js +++ b/cypress/.eslintrc.js @@ -2,15 +2,6 @@ module.exports = { extends: [require.resolve('@backstage/cli/config/eslint.backend')], rules: { 'no-console': 0, - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: false, - peerDependencies: false, - bundledDependencies: false, - }, - ], 'jest/valid-expect': 'off', 'jest/expect-expect': 'off', 'no-restricted-syntax': 'off', diff --git a/packages/app/cypress/.eslintrc.json b/packages/app/cypress/.eslintrc.json index 2b3a458b95..a467608916 100644 --- a/packages/app/cypress/.eslintrc.json +++ b/packages/app/cypress/.eslintrc.json @@ -7,15 +7,6 @@ { "assertFunctionNames": ["expect", "cy.contains"] } - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": true, - "optionalDependencies": true, - "peerDependencies": true, - "bundledDependencies": true - } ] } } diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index e6b5a15336..5cbed2c3df 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -40,7 +40,7 @@ import { } from '@backstage/backend-plugin-api'; // Internal import of the type to avoid needing to export this. -// eslint-disable-next-line monorepo/no-internal-import +// eslint-disable-next-line @backstage/no-forbidden-package-imports import type { InternalSharedBackendEnvironment } from '@backstage/backend-plugin-api/src/wiring/createSharedEnvironment'; export const defaultServiceFactories = [ diff --git a/packages/cli/asset-types/asset-types.d.ts b/packages/cli/asset-types/asset-types.d.ts index 0bd0922004..9bd158bb8e 100644 --- a/packages/cli/asset-types/asset-types.d.ts +++ b/packages/cli/asset-types/asset-types.d.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /// /// diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js index a40aa79fb3..f344af4ab0 100644 --- a/packages/cli/config/eslint-factory.js +++ b/packages/cli/config/eslint-factory.js @@ -60,11 +60,11 @@ function createConfig(dir, extraConfig = {}) { '@spotify/eslint-config-typescript', 'prettier', 'plugin:jest/recommended', - 'plugin:monorepo/recommended', + 'plugin:@backstage/recommended', ...(extraExtends ?? []), ], parser: '@typescript-eslint/parser', - plugins: ['import', ...(plugins ?? [])], + plugins: ['import', 'monorepo', ...(plugins ?? [])], env: { jest: true, ...env, @@ -87,33 +87,8 @@ function createConfig(dir, extraConfig = {}) { '@typescript-eslint/no-shadow': 'error', '@typescript-eslint/no-redeclare': 'error', 'no-undef': 'off', + 'monorepo/no-relative-import': 'error', 'import/newline-after-import': 'error', - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: dir - ? [ - `!${joinPath(dir, 'src/**')}`, - joinPath(dir, 'src/**/*.test.*'), - joinPath(dir, 'src/**/*.stories.*'), - joinPath(dir, 'src/**/__testUtils__/**'), - joinPath(dir, 'src/**/__mocks__/**'), - joinPath(dir, 'src/setupTests.*'), - ] - : [ - // Legacy config for packages that don't provide a dir - '**/*.test.*', - '**/*.stories.*', - '**/__testUtils__/**', - '**/__mocks__/**', - '**/src/setupTests.*', - '**/dev/**', - ], - optionalDependencies: true, - peerDependencies: true, - bundledDependencies: true, - }, - ], 'no-unused-expressions': 'off', '@typescript-eslint/no-unused-expressions': 'error', '@typescript-eslint/consistent-type-assertions': 'error', diff --git a/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts b/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts index 2533131a40..cf37c2a664 100644 --- a/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts +++ b/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts @@ -50,7 +50,7 @@ export async function buildTypeDefinitionsWorker( */ const { PackageJsonLookup, - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line @backstage/no-undeclared-imports } = require('@rushstack/node-core-library/lib/PackageJsonLookup'); const old = PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor; diff --git a/packages/cli/src/lib/version.test.ts b/packages/cli/src/lib/version.test.ts index fb9bf83e7c..604129679b 100644 --- a/packages/cli/src/lib/version.test.ts +++ b/packages/cli/src/lib/version.test.ts @@ -17,7 +17,6 @@ import mockFs from 'mock-fs'; import { packageVersions, createPackageVersionProvider } from './version'; import { Lockfile } from './versioning'; -// eslint-disable-next-line monorepo/no-internal-import import corePluginApiPkg from '@backstage/core-plugin-api/package.json'; describe('createPackageVersionProvider', () => { diff --git a/packages/codemods/.eslintrc.js b/packages/codemods/.eslintrc.js index f9fb2e27c9..e2a53a6ad2 100644 --- a/packages/codemods/.eslintrc.js +++ b/packages/codemods/.eslintrc.js @@ -1,14 +1 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { - rules: { - 'no-console': 0, - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: false, - peerDependencies: false, - bundledDependencies: false, - }, - ], - }, -}); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/codemods/package.json b/packages/codemods/package.json index cbcc0613d4..1ae4d04485 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -35,6 +35,7 @@ "dependencies": { "@backstage/cli-common": "workspace:^", "chalk": "^4.0.0", + "commander": "^9.1.0", "jscodeshift": "^0.14.0", "jscodeshift-add-imports": "^1.0.10" }, @@ -42,7 +43,6 @@ "@backstage/cli": "workspace:^", "@types/jscodeshift": "^0.11.0", "@types/node": "^16.11.26", - "commander": "^9.1.0", "ts-node": "^10.0.0" }, "nodemonConfig": { diff --git a/packages/core-components/src/components/LogViewer/RealLogViewer.test.tsx b/packages/core-components/src/components/LogViewer/RealLogViewer.test.tsx index dd707778e1..e1aac76381 100644 --- a/packages/core-components/src/components/LogViewer/RealLogViewer.test.tsx +++ b/packages/core-components/src/components/LogViewer/RealLogViewer.test.tsx @@ -18,7 +18,7 @@ import React, { ReactNode } from 'react'; import userEvent from '@testing-library/user-event'; import { renderInTestApp } from '@backstage/test-utils'; import { RealLogViewer } from './RealLogViewer'; -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import copyToClipboard from 'copy-to-clipboard'; // Used by useCopyToClipboard diff --git a/packages/core-components/src/components/LogViewer/useLogViewerSelection.test.tsx b/packages/core-components/src/components/LogViewer/useLogViewerSelection.test.tsx index 7e00d50f33..93a6dfd7e9 100644 --- a/packages/core-components/src/components/LogViewer/useLogViewerSelection.test.tsx +++ b/packages/core-components/src/components/LogViewer/useLogViewerSelection.test.tsx @@ -20,7 +20,7 @@ import { TestApiProvider, MockErrorApi } from '@backstage/test-utils'; import { errorApiRef } from '@backstage/core-plugin-api'; import { AnsiLine } from './AnsiProcessor'; import { useLogViewerSelection } from './useLogViewerSelection'; -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import copyToClipboard from 'copy-to-clipboard'; // Used by useCopyToClipboard diff --git a/packages/create-app/bin/backstage-create-app b/packages/create-app/bin/backstage-create-app index 60dfd4990a..4639ea96dc 100755 --- a/packages/create-app/bin/backstage-create-app +++ b/packages/create-app/bin/backstage-create-app @@ -25,7 +25,7 @@ if (!isLocal || process.env.BACKSTAGE_E2E_CLI_TEST) { require('..'); } else { // Only used for development, so should be a devDependency - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line @backstage/no-undeclared-imports require('ts-node').register({ transpileOnly: true, project: path.resolve(__dirname, '../../../tsconfig.json'), diff --git a/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json b/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json index 2b3a458b95..a467608916 100644 --- a/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json +++ b/packages/create-app/templates/default-app/packages/app/cypress/.eslintrc.json @@ -7,15 +7,6 @@ { "assertFunctionNames": ["expect", "cy.contains"] } - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": true, - "optionalDependencies": true, - "peerDependencies": true, - "bundledDependencies": true - } ] } } diff --git a/packages/e2e-test/.eslintrc.js b/packages/e2e-test/.eslintrc.js index f9fb2e27c9..e2a53a6ad2 100644 --- a/packages/e2e-test/.eslintrc.js +++ b/packages/e2e-test/.eslintrc.js @@ -1,14 +1 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { - rules: { - 'no-console': 0, - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: false, - peerDependencies: false, - bundledDependencies: false, - }, - ], - }, -}); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 374eaa6d18..ee447a1736 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -26,22 +26,24 @@ "clean": "backstage-cli package clean" }, "bin": "bin/e2e-test", - "devDependencies": { - "@backstage/cli": "workspace:^", + "dependencies": { "@backstage/cli-common": "workspace:^", "@backstage/errors": "workspace:^", - "@types/fs-extra": "^9.0.1", - "@types/node": "^16.11.26", - "@types/puppeteer": "^5.4.4", "chalk": "^4.0.0", "commander": "^9.1.0", "cross-fetch": "^3.1.5", "fs-extra": "10.1.0", "handlebars": "^4.7.3", - "nodemon": "^2.0.2", "pgtools": "^0.3.0", "puppeteer": "^17.0.0", - "tree-kill": "^1.2.2", + "tree-kill": "^1.2.2" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/fs-extra": "^9.0.1", + "@types/node": "^16.11.26", + "@types/puppeteer": "^5.4.4", + "nodemon": "^2.0.2", "ts-node": "^10.0.0" }, "nodemonConfig": { diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index 3eedb90b21..254198ebb4 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -159,7 +159,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { appendDeps(pkg); } - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line @backstage/no-forbidden-package-imports appendDeps(require('@backstage/create-app/package.json')); print(`Preparing workspace`); diff --git a/packages/repo-tools/src/commands/type-deps/type-deps.ts b/packages/repo-tools/src/commands/type-deps/type-deps.ts index d4dd28aa14..4f8e5e5529 100644 --- a/packages/repo-tools/src/commands/type-deps/type-deps.ts +++ b/packages/repo-tools/src/commands/type-deps/type-deps.ts @@ -17,7 +17,7 @@ import fs from 'fs'; import { resolve as resolvePath } from 'path'; // Cba polluting root package.json, we'll have this -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import chalk from 'chalk'; import { getPackages, Package } from '@manypkg/get-packages'; diff --git a/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json b/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json index 2b3a458b95..a467608916 100644 --- a/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json +++ b/packages/techdocs-cli-embedded-app/cypress/.eslintrc.json @@ -7,15 +7,6 @@ { "assertFunctionNames": ["expect", "cy.contains"] } - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": true, - "optionalDependencies": true, - "peerDependencies": true, - "bundledDependencies": true - } ] } } diff --git a/packages/techdocs-cli/cypress/.eslintrc.json b/packages/techdocs-cli/cypress/.eslintrc.json index 2481ac5715..06965a82da 100644 --- a/packages/techdocs-cli/cypress/.eslintrc.json +++ b/packages/techdocs-cli/cypress/.eslintrc.json @@ -7,15 +7,6 @@ { "assertFunctionNames": ["expect", "cy.contains", "cy.document"] } - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": true, - "optionalDependencies": true, - "peerDependencies": true, - "bundledDependencies": true - } ] } } diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts index 08d95f4417..2a58227862 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import { databaseFactory, discoveryFactory, diff --git a/plugins/code-climate/dev/index.tsx b/plugins/code-climate/dev/index.tsx index d5fc4c28e3..060679337e 100644 --- a/plugins/code-climate/dev/index.tsx +++ b/plugins/code-climate/dev/index.tsx @@ -15,7 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import { createDevApp, EntityGridItem } from '@backstage/dev-utils'; import { Grid } from '@material-ui/core'; import React from 'react'; diff --git a/plugins/code-climate/src/setupTests.ts b/plugins/code-climate/src/setupTests.ts index 43d9e09713..eb4654b869 100644 --- a/plugins/code-climate/src/setupTests.ts +++ b/plugins/code-climate/src/setupTests.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ import '@testing-library/jest-dom'; import 'cross-fetch/polyfill'; diff --git a/plugins/cost-insights/src/example/templates/CostInsightsClient.ts b/plugins/cost-insights/src/example/templates/CostInsightsClient.ts index 50c3bb1470..046196f26b 100644 --- a/plugins/cost-insights/src/example/templates/CostInsightsClient.ts +++ b/plugins/cost-insights/src/example/templates/CostInsightsClient.ts @@ -22,7 +22,7 @@ // IMPORTANT: Remove the lines below to enable type checking and linting // @ts-nocheck -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ import { CostInsightsApi, diff --git a/plugins/example-todo-list/.eslintrc.js b/plugins/example-todo-list/.eslintrc.js index 8c18e76229..e2a53a6ad2 100644 --- a/plugins/example-todo-list/.eslintrc.js +++ b/plugins/example-todo-list/.eslintrc.js @@ -1,14 +1 @@ -module.exports = { - extends: [require.resolve('@backstage/cli/config/eslint')], - rules: { - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: true, - peerDependencies: true, - bundledDependencies: true, - }, - ], - }, -}; +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/scaffolder-backend/scripts/build-nunjucks.js b/plugins/scaffolder-backend/scripts/build-nunjucks.js index 8b98ac4305..8183de77ee 100755 --- a/plugins/scaffolder-backend/scripts/build-nunjucks.js +++ b/plugins/scaffolder-backend/scripts/build-nunjucks.js @@ -16,7 +16,7 @@ */ /* eslint-disable no-restricted-syntax */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const path = require('path'); diff --git a/plugins/splunk-on-call/src/components/TriggerDialog/testUtils.ts b/plugins/splunk-on-call/src/components/TriggerDialog/testUtils.ts index 764ac94e2c..9beb4e150e 100644 --- a/plugins/splunk-on-call/src/components/TriggerDialog/testUtils.ts +++ b/plugins/splunk-on-call/src/components/TriggerDialog/testUtils.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies +// eslint-disable-next-line @backstage/no-undeclared-imports import { act, fireEvent, diff --git a/scripts/assemble-manifest.js b/scripts/assemble-manifest.js index f68a83447d..f01bc7fd9b 100755 --- a/scripts/assemble-manifest.js +++ b/scripts/assemble-manifest.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /* * Copyright 2022 The Backstage Authors * diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js index 10c4b99976..3dcbd6ef81 100755 --- a/scripts/check-docs-quality.js +++ b/scripts/check-docs-quality.js @@ -56,7 +56,7 @@ async function listFiles(dir = '') { // caused by the script. In CI, we want to ensure vale linter is run. async function exitIfMissingVale() { try { - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line @backstage/no-undeclared-imports await require('command-exists')('vale'); } catch (e) { if (process.env.CI) { diff --git a/scripts/check-if-release.js b/scripts/check-if-release.js index e7dabe14a4..c12b27b0ef 100755 --- a/scripts/check-if-release.js +++ b/scripts/check-if-release.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /* * Copyright 2020 The Backstage Authors * diff --git a/scripts/create-github-release.js b/scripts/create-github-release.js index 4b3891c5ce..b64d7a9cb5 100755 --- a/scripts/create-github-release.js +++ b/scripts/create-github-release.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /* * Copyright 2020 The Backstage Authors * diff --git a/scripts/create-release-tag.js b/scripts/create-release-tag.js index 28417024fd..311b46d4e5 100755 --- a/scripts/create-release-tag.js +++ b/scripts/create-release-tag.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /* * Copyright 2020 The Backstage Authors * diff --git a/scripts/list-deprecations.js b/scripts/list-deprecations.js index 8047158138..22aff96d67 100755 --- a/scripts/list-deprecations.js +++ b/scripts/list-deprecations.js @@ -15,7 +15,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const _ = require('lodash'); const fs = require('fs-extra'); diff --git a/scripts/prepare-release.js b/scripts/prepare-release.js index 973f4fad58..4019a71cf3 100755 --- a/scripts/prepare-release.js +++ b/scripts/prepare-release.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ /* * Copyright 2022 The Backstage Authors * diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index da64e2ea82..ea7d42a2a4 100755 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ import { Octokit } from '@octokit/rest'; import minimist from 'minimist'; // Generated by GitHub workflow .github/workflows/snyk-github-issue-creator diff --git a/scripts/verify-api-reference.js b/scripts/verify-api-reference.js index 84a526604d..091e803c33 100755 --- a/scripts/verify-api-reference.js +++ b/scripts/verify-api-reference.js @@ -15,7 +15,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const { resolve: resolvePath } = require('path'); const { promises: fs } = require('fs'); diff --git a/scripts/verify-changesets.js b/scripts/verify-changesets.js index 9e3064eeac..ae3f07840a 100755 --- a/scripts/verify-changesets.js +++ b/scripts/verify-changesets.js @@ -15,7 +15,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const { resolve: resolvePath } = require('path'); const fs = require('fs-extra'); diff --git a/scripts/verify-links.js b/scripts/verify-links.js index 05ad387c37..7664cadad5 100755 --- a/scripts/verify-links.js +++ b/scripts/verify-links.js @@ -15,7 +15,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const { resolve: resolvePath, join: joinPath, dirname } = require('path'); const fs = require('fs').promises; diff --git a/scripts/verify-lockfile-duplicates.js b/scripts/verify-lockfile-duplicates.js index cc22664219..820fba2ea5 100644 --- a/scripts/verify-lockfile-duplicates.js +++ b/scripts/verify-lockfile-duplicates.js @@ -15,7 +15,7 @@ * limitations under the License. */ -/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @backstage/no-undeclared-imports */ const { execFile: execFileCb } = require('child_process'); const { resolve: resolvePath, dirname: dirnamePath } = require('path'); From 4753d26b949a521c48334882c6bac4eb6cd2a2c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:42:23 +0100 Subject: [PATCH 067/130] eslint-plugin: add no-relative-monorepo-imports rule Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/index.js | 2 + packages/eslint-plugin/lib/getPackages.js | 10 ++- .../rules/no-relative-monorepo-imports.js | 80 +++++++++++++++++++ .../rules/no-undeclared-imports.js | 4 +- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 packages/eslint-plugin/rules/no-relative-monorepo-imports.js diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index cf1dd20ab7..20a0ff2d18 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -20,12 +20,14 @@ module.exports = { plugins: ['@backstage'], rules: { '@backstage/no-forbidden-package-imports': 'error', + '@backstage/no-relative-monorepo-imports': 'error', '@backstage/no-undeclared-imports': 'error', }, }, }, rules: { 'no-forbidden-package-imports': require('./rules/no-forbidden-package-imports'), + 'no-relative-monorepo-imports': require('./rules/no-relative-monorepo-imports'), 'no-undeclared-imports': require('./rules/no-undeclared-imports'), }, }; diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 318d0654c2..6c8c1a66b2 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -16,7 +16,7 @@ // @ts-check -/** @type {import('@manypkg/get-packages')} */ +const path = require('path'); const manypkg = require('@manypkg/get-packages'); /** @@ -31,6 +31,7 @@ const manypkg = require('@manypkg/get-packages'); * @property {ExtendedPackage} root * @property {ExtendedPackage[]} list * @property {Map} map + * @property {(path: string) => ExtendedPackage | undefined} byPath */ // Loads all packages in the monorepo once, and caches the result @@ -56,6 +57,13 @@ module.exports = (function () { map: new Map(packages.packages.map(pkg => [pkg.packageJson.name, pkg])), list: packages.packages, root: packages.root, + byPath(filePath) { + return packages.packages.find(pkg => { + if (!path.relative(pkg.dir, filePath).startsWith('..')) { + return pkg; + } + }); + }, }; lastLoadAt = Date.now(); return result; diff --git a/packages/eslint-plugin/rules/no-relative-monorepo-imports.js b/packages/eslint-plugin/rules/no-relative-monorepo-imports.js new file mode 100644 index 0000000000..cb18eb506c --- /dev/null +++ b/packages/eslint-plugin/rules/no-relative-monorepo-imports.js @@ -0,0 +1,80 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +const path = require('path'); +const visitImports = require('../lib/visitImports'); +const getPackageMap = require('../lib/getPackages'); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + messages: { + outside: 'Import of {{path}} is outside of any known monorepo package', + forbidden: + "Relative imports of monorepo packages are forbidden, use '{{newImport}}' instead", + }, + }, + create(context) { + const packages = getPackageMap(context.getCwd()); + if (!packages) { + return {}; + } + const filePath = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(); + + const localPkg = packages.byPath(filePath); + if (!localPkg) { + return {}; + } + + return visitImports(context, (node, imp) => { + if (imp.type !== 'local') { + return; + } + + const target = path.resolve(path.dirname(filePath), imp.path); + if (!path.relative(localPkg.dir, target).startsWith('..')) { + return; + } + + const targetPkg = packages.byPath(target); + if (!targetPkg) { + context.report({ + node: node, + messageId: 'outside', + data: { + path: target, + }, + }); + return; + } + + const targetPath = path.relative(targetPkg.dir, target); + const targetName = targetPkg.packageJson.name ?? ''; + context.report({ + node: node, + messageId: 'forbidden', + data: { + newImport: targetPath ? `${targetName}/${targetPath}` : targetName, + }, + }); + }); + }, +}; diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index f7f7b17354..4a8756b71e 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -135,9 +135,7 @@ module.exports = { ? context.getPhysicalFilename() : context.getFilename(); - const localPkg = packages.list.find(p => - filePath.startsWith(p.dir + path.sep), - ); + const localPkg = packages.byPath(filePath); if (!localPkg) { return {}; } From 906af5db533ee9356b61a7f3844339d3d77cbbb4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 16:48:43 +0100 Subject: [PATCH 068/130] cli: remove unnecessary lint rules and fix issues Signed-off-by: Patrik Oldsberg --- packages/cli/config/eslint-factory.js | 8 +------- packages/cli/package.json | 1 - packages/cli/src/lib/version.ts | 2 +- packages/core-plugin-api/src/app/types.ts | 2 +- packages/create-app/src/index.ts | 1 + packages/create-app/src/lib/versions.ts | 2 +- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js index f344af4ab0..dde853fdff 100644 --- a/packages/cli/config/eslint-factory.js +++ b/packages/cli/config/eslint-factory.js @@ -64,7 +64,7 @@ function createConfig(dir, extraConfig = {}) { ...(extraExtends ?? []), ], parser: '@typescript-eslint/parser', - plugins: ['import', 'monorepo', ...(plugins ?? [])], + plugins: ['import', ...(plugins ?? [])], env: { jest: true, ...env, @@ -87,7 +87,6 @@ function createConfig(dir, extraConfig = {}) { '@typescript-eslint/no-shadow': 'error', '@typescript-eslint/no-redeclare': 'error', 'no-undef': 'off', - 'monorepo/no-relative-import': 'error', 'import/newline-after-import': 'error', 'no-unused-expressions': 'off', '@typescript-eslint/no-unused-expressions': 'error', @@ -110,9 +109,6 @@ function createConfig(dir, extraConfig = {}) { ...(restrictedSrcImports ?? []), ], patterns: [ - // Avoid cross-package imports - '**/../../**/*/src/**', - '**/../../**/*/src', // Prevent imports of stories or tests '*.stories*', '*.test*', @@ -162,8 +158,6 @@ function createConfig(dir, extraConfig = {}) { ...(restrictedImports ?? []), ...(restrictedTestImports ?? []), ], - // Avoid cross-package imports - patterns: ['**/../../**/*/src/**', '**/../../**/*/src'], }, ], }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 909ac8460c..359f16adfa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,7 +76,6 @@ "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^27.0.0", "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-monorepo": "^0.3.2", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", "eslint-webpack-plugin": "^3.1.1", diff --git a/packages/cli/src/lib/version.ts b/packages/cli/src/lib/version.ts index 5a199236f5..bf89842d5f 100644 --- a/packages/cli/src/lib/version.ts +++ b/packages/cli/src/lib/version.ts @@ -19,7 +19,7 @@ import semver from 'semver'; import { paths } from './paths'; import { Lockfile } from './versioning'; -/* eslint-disable monorepo/no-relative-import */ +/* eslint-disable @backstage/no-relative-monorepo-imports */ /* This is a list of all packages used by the templates. If dependencies are added or removed, diff --git a/packages/core-plugin-api/src/app/types.ts b/packages/core-plugin-api/src/app/types.ts index 2a71eda1f4..f3e0547ca4 100644 --- a/packages/core-plugin-api/src/app/types.ts +++ b/packages/core-plugin-api/src/app/types.ts @@ -17,7 +17,7 @@ // This is a bit of a hack that we use to avoid having to redeclare these types // within this package or have an explicit dependency on core-app-api. // These types end up being inlined and duplicated into this package at build time. -// eslint-disable-next-line no-restricted-imports +// eslint-disable-next-line @backstage/no-relative-monorepo-imports export type { BootErrorPageProps, SignInPageProps, diff --git a/packages/create-app/src/index.ts b/packages/create-app/src/index.ts index 216fab6740..fa235e6175 100644 --- a/packages/create-app/src/index.ts +++ b/packages/create-app/src/index.ts @@ -22,6 +22,7 @@ import { program } from 'commander'; import { exitWithError } from './lib/errors'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports import { version } from '../../../package.json'; import createApp from './createApp'; diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index b89b1d5463..72f5c2c9d5 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -/* eslint-disable monorepo/no-relative-import */ +/* eslint-disable @backstage/no-relative-monorepo-imports */ /* This is a list of all packages used by the template. If dependencies are added or removed, From ef73e8626bf906febc682f1f31451a4668e329c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 17:04:24 +0100 Subject: [PATCH 069/130] eslint-plugin: add docs for no-undeclared-imports Signed-off-by: Patrik Oldsberg --- .../docs/rules/no-undeclared-imports.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-undeclared-imports.md diff --git a/packages/eslint-plugin/docs/rules/no-undeclared-imports.md b/packages/eslint-plugin/docs/rules/no-undeclared-imports.md new file mode 100644 index 0000000000..6102c772e6 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-undeclared-imports.md @@ -0,0 +1,84 @@ +# @backstage/no-undeclared-imports + +Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. + +## Usage + +Add the rules as follows, it has no options: + +```js +"@backstage/no-undeclared-imports": ["error"] +``` + +The following patterns are considered files used during development, and only need dependencies to be declared in devDependencies: + +```python +!src/** # Any files outside of src are considered dev files +src/**/*.test.* +src/**/*.stories.* +src/**/__testUtils__/** +src/**/__mocks__/** +src/setupTests.* +``` + +## Rule Details + +Given the following `package.json`: + +```json +{ + "name": "@backstage/plugin-foo", + "backstage": { + "role": "frontend-plugin" + }, + "dependencies": { + "react": "^17.0.0" + }, + "devDependencies": { + "@backstage/core-plugin-api": "^1.0.0" + }, + "peerDependencies": { + "@backstage/config": "*" + } +} +``` + +### Fail + +Inside `src/my-plugin.ts`: + +```ts +// Should be declared as a dependency +const _ = require('lodash'); +import _ from 'lodash'; + +// React should be a peer dependency in frontend plugins +import react from 'react'; + +// Should be declared as a dependency, not a dev dependency +import { useApi } from '@backstage/core-plugin-api'; +``` + +Inside `src/my-plugin.test.ts` (a test file): + +```ts +// Should be declared as a dev dependency +const _ = require('lodash'); +import _ from 'lodash'; +``` + +### Pass + +Inside `src/my-plugin.ts`: + +```ts +// Declared in peerDependencies, so it is allowed +import { ConfigReader } from '@backstage/config'; +``` + +Inside `src/my-plugin.test.ts` (a test file): + +```ts +// Declared as a dev dependency inside a test file +import { useApi } from '@backstage/core-plugin-api'; +``` From 5105b9848a6ed57512697f3c80d198dcdabfdbae Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 17:10:51 +0100 Subject: [PATCH 070/130] eslint-plugin: add docs for no-relative-monorepo-imports Signed-off-by: Patrik Oldsberg --- .../rules/no-relative-monorepo-imports.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md diff --git a/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md b/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md new file mode 100644 index 0000000000..6a41a73fb6 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md @@ -0,0 +1,43 @@ +# @backstage/no-relative-monorepo-imports + +Forbid relative imports that reach outside of the package in a monorepo. + +## Usage + +Add the rules as follows, it has no options: + +```js +"@backstage/no-relative-monorepo-imports": ["error"] +``` + +The following patterns are considered files used during development, and only need dependencies to be declared in devDependencies: + +```python +!src/** # Any files outside of src are considered dev files +src/**/*.test.* +src/**/*.stories.* +src/**/__testUtils__/** +src/**/__mocks__/** +src/setupTests.* +``` + +## Rule Details + +Assuming an import from for example `plugins/bar/src/index.ts`: + +### Fail + +```ts +import { FooCard } from '../../foo'; + +import { FooCard } from '../../foo/src/components/FooCard'; +``` + +### Pass + +```ts +import { FooCard } from '@internal/plugin-foo'; + +// This is allowed by this rule, but not by no-forbidden-package-imports +import { FooCard } from '@internal/plugin-foo/src/components/FooCard'; +``` From 015c0e0f89025c7ad8925a2bf7ad5321efaf4adf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 17:15:55 +0100 Subject: [PATCH 071/130] eslint-plugin: add docs for no-forbidden-package-imports Signed-off-by: Patrik Oldsberg --- .../rules/no-forbidden-package-imports.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-forbidden-package-imports.md diff --git a/packages/eslint-plugin/docs/rules/no-forbidden-package-imports.md b/packages/eslint-plugin/docs/rules/no-forbidden-package-imports.md new file mode 100644 index 0000000000..7a6ae11c37 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-forbidden-package-imports.md @@ -0,0 +1,52 @@ +# @backstage/no-forbidden-package-imports + +Disallow internal monorepo imports from package subpaths that are not exported. + +## Usage + +Add the rules as follows, it has no options: + +```js +"@backstage/no-forbidden-package-imports": ["error"] +``` + +## Rule Details + +Given the following two target packages: + +```json +{ + "name": "@backstage/plugin-foo", + "files": ["dist", "type-utils"] +} +``` + +```json +{ + "name": "@backstage/plugin-bar", + "exports": { + ".": "./src/index.ts", + "./testUtils": "./src/testUtils/index.ts", + "./package.json": "./package.json" + } +} +``` + +### Fail + +```ts +import { FooCard } from '@backstage/plugin-foo/src/components'; +import { BarCard } from '@backstage/plugin-bar/src/components'; +``` + +### Pass + +```ts +import { FooCard } from '@backstage/plugin-foo'; +import { FooType } from '@backstage/plugin-foo/type-utils'; +import fooPkg from '@backstage/plugin-foo/package.json'; + +import { BarCard } from '@backstage/plugin-bar'; +import { renderBarCardExtension } from '@backstage/plugin-bar/testUtils'; +import barPkg from '@backstage/plugin-bar/package.json'; +``` From 71bf2eb0ed63c7fc3482d3d043e055ac865cb418 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 17:20:24 +0100 Subject: [PATCH 072/130] eslint-plugin: add doc rule links and update README Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/README.md | 8 +++++--- .../eslint-plugin/rules/no-forbidden-package-imports.js | 5 +++++ .../eslint-plugin/rules/no-relative-monorepo-imports.js | 5 +++++ packages/eslint-plugin/rules/no-undeclared-imports.js | 5 +++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index a063d801d3..c311670603 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -35,6 +35,8 @@ rules: { The following rules are provided by this plugin: -| Rule | Description | -| ----------------------------------------- | ------------------------------------------------------------------------------- | -| `@backstage/no-forbidden-package-imports` | Disallow internal monorepo imports from package subpaths that are not exported. | +| Rule | Description | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| [@backstage/no-forbidden-package-imports](./docs/rules/no-forbidden-package-imports.md) | Disallow internal monorepo imports from package subpaths that are not exported. | +| [@backstage/no-relative-monorepo-imports](./docs/rules/no-relative-monorepo-imports.md) | Forbid relative imports that reach outside of the package in a monorepo. | +| [@backstage/no-undeclared-imports](./docs/rules/no-undeclared-imports.md) | Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. | diff --git a/packages/eslint-plugin/rules/no-forbidden-package-imports.js b/packages/eslint-plugin/rules/no-forbidden-package-imports.js index 6c861e17f5..e7c1acca27 100644 --- a/packages/eslint-plugin/rules/no-forbidden-package-imports.js +++ b/packages/eslint-plugin/rules/no-forbidden-package-imports.js @@ -25,6 +25,11 @@ module.exports = { messages: { forbidden: '{{packageName}} does not export {{subPath}}', }, + docs: { + description: + 'Disallow internal monorepo imports from package subpaths that are not exported.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-forbidden-package-imports.md', + }, }, create(context) { return visitImports(context, (node, imp) => { diff --git a/packages/eslint-plugin/rules/no-relative-monorepo-imports.js b/packages/eslint-plugin/rules/no-relative-monorepo-imports.js index cb18eb506c..df30d35f67 100644 --- a/packages/eslint-plugin/rules/no-relative-monorepo-imports.js +++ b/packages/eslint-plugin/rules/no-relative-monorepo-imports.js @@ -29,6 +29,11 @@ module.exports = { forbidden: "Relative imports of monorepo packages are forbidden, use '{{newImport}}' instead", }, + docs: { + description: + 'Forbid relative imports that reach outside of the package in a monorepo.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md', + }, }, create(context) { const packages = getPackageMap(context.getCwd()); diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index 4a8756b71e..4f60b712d9 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -125,6 +125,11 @@ module.exports = { switch: '{{ packageName }} is declared in {{ oldDepsField }}, but should be moved to {{ depsField }} in {{ packageJsonPath }}.', }, + docs: { + description: + 'Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-undeclared-imports.md', + }, }, create(context) { const packages = getPackageMap(context.getCwd()); From dd8a9afe668ab0bb69bc2d65130e513ce58e8426 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 17:24:49 +0100 Subject: [PATCH 073/130] added and updated changesets for eslint-plugin addition Signed-off-by: Patrik Oldsberg --- .changeset/few-moons-accept.md | 5 +++++ .changeset/tough-jeans-camp.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/few-moons-accept.md diff --git a/.changeset/few-moons-accept.md b/.changeset/few-moons-accept.md new file mode 100644 index 0000000000..c790e837c8 --- /dev/null +++ b/.changeset/few-moons-accept.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Replaced several monorepo lint rules with new rules from `@backstage/eslint-plugin`. See the [README](https://github.com/import-js/eslint-plugin-import/blob/main/packages/eslint-plugin/README.md) for a full list of rules. diff --git a/.changeset/tough-jeans-camp.md b/.changeset/tough-jeans-camp.md index b7c08593e1..8c7a53190f 100644 --- a/.changeset/tough-jeans-camp.md +++ b/.changeset/tough-jeans-camp.md @@ -2,4 +2,4 @@ '@backstage/eslint-plugin': minor --- -Added a new ESLint plugin with common rules for Backstage projects. +Added a new ESLint plugin with common rules for Backstage projects. See the [README](https://github.com/import-js/eslint-plugin-import/blob/main/packages/eslint-plugin/README.md) for more details. From 1b30c9a0f7f77a16e16c238c324b6c6bf4f3b1d4 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 3 Feb 2023 19:59:51 +0100 Subject: [PATCH 074/130] feat(search): improve router query errors Signed-off-by: Camila Belo --- .changeset/hip-onions-tan.md | 5 +++++ .../search-backend/src/service/router.test.ts | 17 +++++++++++++++++ plugins/search-backend/src/service/router.ts | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/hip-onions-tan.md diff --git a/.changeset/hip-onions-tan.md b/.changeset/hip-onions-tan.md new file mode 100644 index 0000000000..01be0d4867 --- /dev/null +++ b/.changeset/hip-onions-tan.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend': patch +--- + +Change the router's response to include the error message instead of its object type in case it fails during a search query because the messages have more info. diff --git a/plugins/search-backend/src/service/router.test.ts b/plugins/search-backend/src/service/router.test.ts index 1dbd145bce..b4772dd62d 100644 --- a/plugins/search-backend/src/service/router.test.ts +++ b/plugins/search-backend/src/service/router.test.ts @@ -74,6 +74,23 @@ describe('createRouter', () => { }); describe('GET /query', () => { + it('throws meaningful query errors', async () => { + const error = new Error('Query error message'); + mockSearchEngine.query.mockRejectedValueOnce(error); + + const response = await request(app).get('/query'); + + expect(response.status).toEqual(500); + expect(response.body).toMatchObject( + expect.objectContaining({ + error: { + name: 'Error', + message: `There was a problem performing the search query: ${error.message}`, + }, + }), + ); + }); + it('returns empty results array', async () => { const response = await request(app).get('/query'); diff --git a/plugins/search-backend/src/service/router.ts b/plugins/search-backend/src/service/router.ts index b51dcead14..10f47d180f 100644 --- a/plugins/search-backend/src/service/router.ts +++ b/plugins/search-backend/src/service/router.ts @@ -184,7 +184,7 @@ export async function createRouter( } throw new Error( - `There was a problem performing the search query. ${error}`, + `There was a problem performing the search query: ${error.message}`, ); } }, From d5fe9f10934ad24fee2b5db055ee502f0ee349bb Mon Sep 17 00:00:00 2001 From: Mark Anderson-Trocme Date: Fri, 3 Feb 2023 16:09:28 -0500 Subject: [PATCH 075/130] feature: build workflow url Signed-off-by: Mark Anderson-Trocme --- .../src/components/BuildsPage/lib/CITable/CITable.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx index 8f99f1f69c..6b22d641ee 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -214,7 +214,14 @@ const generatedColumns: TableColumn[] = [ field: 'workflow.name', highlight: true, render: (row: Partial) => ( - {row?.workflow?.name} + + {row?.workflow?.name} + ), }, { From c044a3629b6549981c0b6ae50561cef9bf21ba60 Mon Sep 17 00:00:00 2001 From: Mark Anderson-Trocme Date: Fri, 3 Feb 2023 16:16:47 -0500 Subject: [PATCH 076/130] lint Signed-off-by: Mark Anderson-Trocme --- .../src/components/BuildsPage/lib/CITable/CITable.tsx | 5 +---- 1 file changed, 1 insertion(+), 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 6b22d641ee..22f69ac30f 100644 --- a/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/circleci/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -215,10 +215,7 @@ const generatedColumns: TableColumn[] = [ highlight: true, render: (row: Partial) => ( {row?.workflow?.name} From aefe3a562495c2a1e8efbb926958654d85f0e4fb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 3 Feb 2023 18:06:52 +0100 Subject: [PATCH 077/130] eslint-plugin: added tests for no-relative-monorepo-imports Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/.eslintrc.js | 3 + packages/eslint-plugin/package.json | 6 +- .../src/__fixtures__/monorepo/package.json | 6 + .../monorepo/packages/bar/package.json | 9 ++ .../monorepo/packages/foo/package.json | 7 + .../src/no-relative-monorepo-imports.test.ts | 68 +++++++++ yarn.lock | 140 +----------------- 7 files changed, 102 insertions(+), 137 deletions(-) create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/package.json create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json create mode 100644 packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js index 7fc68c3c5d..01d1b181d7 100644 --- a/packages/eslint-plugin/.eslintrc.js +++ b/packages/eslint-plugin/.eslintrc.js @@ -21,6 +21,9 @@ module.exports = { node: true, es2021: true, }, + parserOptions: { + sourceType: 'module', + }, rules: { '@backstage/no-undeclared-imports': ['error'], 'no-unused-expressions': 'off', diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 73862d2295..84fc16b98f 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -14,13 +14,15 @@ "license": "Apache-2.0", "main": "./index.js", "scripts": { - "lint": "backstage-cli package lint" + "lint": "backstage-cli package lint", + "test": "backstage-cli package test" }, "dependencies": { "@manypkg/get-packages": "^1.1.3", "minimatch": "^5.1.2" }, "devDependencies": { - "@backstage/cli": "workspace:^" + "@backstage/cli": "workspace:^", + "eslint": "^8.33.0" } } diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/package.json new file mode 100644 index 0000000000..c35178c46f --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/package.json @@ -0,0 +1,6 @@ +{ + "name": "root", + "workspaces": [ + "packages/*" + ] +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json new file mode 100644 index 0000000000..8331b20c43 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json @@ -0,0 +1,9 @@ +{ + "name": "@internal/bar", + "backstage": { + "role": "frontend-plugin" + }, + "peerDependencies": { + "react": "*" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json new file mode 100644 index 0000000000..66341563cd --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -0,0 +1,7 @@ +{ + "name": "@internal/foo", + "dependencies": { + "@internal/bar": "1.0.0", + "lodash": "*" + } +} diff --git a/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts new file mode 100644 index 0000000000..6888d258cc --- /dev/null +++ b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2023 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 { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-relative-monorepo-imports'; + +const RULE = 'no-relative-monorepo-imports'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR_OUTSIDE = (path: string) => ({ + message: `Import of ${path} is outside of any known monorepo package`, +}); +const ERR_FORBIDDEN = (newImp: string) => ({ + message: `Relative imports of monorepo packages are forbidden, use '${newImp}' instead`, +}); + +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + { + code: `import { version } from '@internal/foo'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + }, + { + code: `import { version } from '@internal/foo/src'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + }, + ], + invalid: [ + { + code: `import { version } from '../../foo'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_FORBIDDEN('@internal/foo')], + }, + { + code: `import { version } from '../../foo/src'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_FORBIDDEN('@internal/foo/src')], + }, + { + code: `import { version } from '../../../package.json'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_OUTSIDE(path.join(FIXTURE, 'package.json'))], + }, + ], +}); diff --git a/yarn.lock b/yarn.lock index e0e1da349c..2d250e9913 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3721,7 +3721,6 @@ __metadata: eslint-plugin-import: ^2.25.4 eslint-plugin-jest: ^27.0.0 eslint-plugin-jsx-a11y: ^6.5.1 - eslint-plugin-monorepo: ^0.3.2 eslint-plugin-react: ^7.28.0 eslint-plugin-react-hooks: ^4.3.0 eslint-webpack-plugin: ^3.1.1 @@ -4101,6 +4100,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@manypkg/get-packages": ^1.1.3 + eslint: ^8.33.0 minimatch: ^5.1.2 languageName: unknown linkType: soft @@ -16670,15 +16670,6 @@ __metadata: languageName: node linkType: hard -"array-union@npm:^1.0.1": - version: 1.0.2 - resolution: "array-union@npm:1.0.2" - dependencies: - array-uniq: ^1.0.1 - checksum: 82cec6421b6e6766556c484835a6d476a873f1b71cace5ab2b4f1b15b1e3162dc4da0d16f7a2b04d4aec18146c6638fe8f661340b31ba8e469fd811a1b45dc8d - languageName: node - linkType: hard - "array-union@npm:^2.1.0": version: 2.1.0 resolution: "array-union@npm:2.1.0" @@ -16686,13 +16677,6 @@ __metadata: languageName: node linkType: hard -"array-uniq@npm:^1.0.1": - version: 1.0.3 - resolution: "array-uniq@npm:1.0.3" - checksum: 1625f06b093d8bf279b81adfec6e72951c0857d65b5e3f65f053fffe9f9dd61c2fc52cff57e38a4700817e7e3f01a4faa433d505ea9e33cdae4514c334e0bf9e - languageName: node - linkType: hard - "array.prototype.flat@npm:^1.2.3, array.prototype.flat@npm:^1.3.1": version: 1.3.1 resolution: "array.prototype.flat@npm:1.3.1" @@ -20534,15 +20518,6 @@ __metadata: languageName: node linkType: hard -"dir-glob@npm:^2.0.0": - version: 2.2.2 - resolution: "dir-glob@npm:2.2.2" - dependencies: - path-type: ^3.0.0 - checksum: 3aa48714a9f7845ffc30ab03a5c674fe760477cc55e67b0847333371549227d93953e6627ec160f75140c5bea5c5f88d13c01de79bd1997a588efbcf06980842 - languageName: node - linkType: hard - "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -21606,7 +21581,7 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.1.1, eslint-module-utils@npm:^2.7.4": +"eslint-module-utils@npm:^2.7.4": version: 2.7.4 resolution: "eslint-module-utils@npm:2.7.4" dependencies: @@ -21711,21 +21686,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-monorepo@npm:^0.3.2": - version: 0.3.2 - resolution: "eslint-plugin-monorepo@npm:0.3.2" - dependencies: - eslint-module-utils: ^2.1.1 - get-monorepo-packages: ^1.1.0 - globby: ^7.1.1 - load-json-file: ^4.0.0 - minimatch: ^3.0.4 - parse-package-name: ^0.1.0 - path-is-inside: ^1.0.2 - checksum: b6f17efbc9e66aefbf7aacf9aa130bc8e729a6311c412d1b0026ce7d93361c10b0e6e4b10cf434f24ca9a8b2e891bc8e5b82e66dc9ccc5e782f9c18a989eca28 - languageName: node - linkType: hard - "eslint-plugin-notice@npm:^0.9.10": version: 0.9.10 resolution: "eslint-plugin-notice@npm:0.9.10" @@ -21845,7 +21805,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.6.0": +"eslint@npm:^8.33.0, eslint@npm:^8.6.0": version: 8.33.0 resolution: "eslint@npm:8.33.0" dependencies: @@ -23438,16 +23398,6 @@ __metadata: languageName: node linkType: hard -"get-monorepo-packages@npm:^1.1.0": - version: 1.2.0 - resolution: "get-monorepo-packages@npm:1.2.0" - dependencies: - globby: ^7.1.1 - load-json-file: ^4.0.0 - checksum: f9321c11b8e11f02138758db6589d8ab8b7e1b05e78cac92493b635faea10aa100c20fe40f2cf110c82ec100c118c6c131dff4c65d32a721c617dc2928f7b277 - languageName: node - linkType: hard - "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -23598,7 +23548,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": +"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -23707,20 +23657,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^7.1.1": - version: 7.1.1 - resolution: "globby@npm:7.1.1" - dependencies: - array-union: ^1.0.1 - dir-glob: ^2.0.0 - glob: ^7.1.2 - ignore: ^3.3.5 - pify: ^3.0.0 - slash: ^1.0.0 - checksum: f0eba08a08ae7c98149a4411661c0bf08c4717d81e6f355cf624fb01880b249737eb8e951bf86124cb3af8ea1c793c0a9d363ed5cdec99bb2c6b68f8a323025f - languageName: node - linkType: hard - "good-listener@npm:^1.2.2": version: 1.2.2 resolution: "good-listener@npm:1.2.2" @@ -24710,13 +24646,6 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^3.3.5": - version: 3.3.10 - resolution: "ignore@npm:3.3.10" - checksum: 23e8cc776e367b56615ab21b78decf973a35dfca5522b39d9b47643d8168473b0d1f18dd1321a1bab466a12ea11a2411903f3b21644f4d5461ee0711ec8678bd - languageName: node - linkType: hard - "ignore@npm:^5.1.4, ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" @@ -26828,13 +26757,6 @@ __metadata: languageName: node linkType: hard -"json-parse-better-errors@npm:^1.0.1": - version: 1.0.2 - resolution: "json-parse-better-errors@npm:1.0.2" - checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d - languageName: node - linkType: hard - "json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" @@ -27725,18 +27647,6 @@ __metadata: languageName: node linkType: hard -"load-json-file@npm:^4.0.0": - version: 4.0.0 - resolution: "load-json-file@npm:4.0.0" - dependencies: - graceful-fs: ^4.1.2 - parse-json: ^4.0.0 - pify: ^3.0.0 - strip-bom: ^3.0.0 - checksum: 8f5d6d93ba64a9620445ee9bde4d98b1eac32cf6c8c2d20d44abfa41a6945e7969456ab5f1ca2fb06ee32e206c9769a20eec7002fe290de462e8c884b6b8b356 - languageName: node - linkType: hard - "load-yaml-file@npm:^0.2.0": version: 0.2.0 resolution: "load-yaml-file@npm:0.2.0" @@ -30849,16 +30759,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-json@npm:4.0.0" - dependencies: - error-ex: ^1.3.1 - json-parse-better-errors: ^1.0.1 - checksum: 0fe227d410a61090c247e34fa210552b834613c006c2c64d9a05cfe9e89cf8b4246d1246b1a99524b53b313e9ac024438d0680f67e33eaed7e6f38db64cfe7b5 - languageName: node - linkType: hard - "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -30871,13 +30771,6 @@ __metadata: languageName: node linkType: hard -"parse-package-name@npm:^0.1.0": - version: 0.1.0 - resolution: "parse-package-name@npm:0.1.0" - checksum: 7d69b1deeaf82d5a59536c5a8772c8db003b1415dd2465073343dfab5caf90917ecfffdd71d1c004f5cf963c3881ccb8899d0ff820eac705a272e817e246de14 - languageName: node - linkType: hard - "parse-path@npm:^7.0.0": version: 7.0.0 resolution: "parse-path@npm:7.0.0" @@ -31150,7 +31043,7 @@ __metadata: languageName: node linkType: hard -"path-is-inside@npm:1.0.2, path-is-inside@npm:^1.0.2": +"path-is-inside@npm:1.0.2": version: 1.0.2 resolution: "path-is-inside@npm:1.0.2" checksum: 0b5b6c92d3018b82afb1f74fe6de6338c4c654de4a96123cb343f2b747d5606590ac0c890f956ed38220a4ab59baddfd7b713d78a62d240b20b14ab801fa02cb @@ -31249,15 +31142,6 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^3.0.0": - version: 3.0.0 - resolution: "path-type@npm:3.0.0" - dependencies: - pify: ^3.0.0 - checksum: 735b35e256bad181f38fa021033b1c33cfbe62ead42bb2222b56c210e42938eecb272ae1949f3b6db4ac39597a61b44edd8384623ec4d79bfdc9a9c0f12537a6 - languageName: node - linkType: hard - "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -31432,13 +31316,6 @@ __metadata: languageName: node linkType: hard -"pify@npm:^3.0.0": - version: 3.0.0 - resolution: "pify@npm:3.0.0" - checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde - languageName: node - linkType: hard - "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -34985,13 +34862,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^1.0.0": - version: 1.0.0 - resolution: "slash@npm:1.0.0" - checksum: 4b6e21b1fba6184a7e2efb1dd173f692d8a845584c1bbf9dc818ff86f5a52fc91b413008223d17cc684604ee8bb9263a420b1182027ad9762e35388434918860 - languageName: node - linkType: hard - "slash@npm:^2.0.0": version: 2.0.0 resolution: "slash@npm:2.0.0" From 6801eb57805c06c398bd82da72f1429acc634a07 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 10:52:55 +0100 Subject: [PATCH 078/130] eslint-plugin: refactor to fix issues uncovered by more strict parsing Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/lib/getPackages.js | 12 ++++++------ packages/eslint-plugin/lib/visitImports.js | 12 ++++++++++-- .../eslint-plugin/rules/no-undeclared-imports.js | 13 +++++++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 6c8c1a66b2..f5f2c63cc1 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -36,8 +36,10 @@ const manypkg = require('@manypkg/get-packages'); // Loads all packages in the monorepo once, and caches the result module.exports = (function () { + /** @type {PackageMap | undefined} */ let result = undefined; - let lastLoadAt = undefined; + /** @type {number} */ + let lastLoadAt = 0; /** @returns {PackageMap | undefined} */ return function getPackages(/** @type {string} */ dir) { @@ -58,11 +60,9 @@ module.exports = (function () { list: packages.packages, root: packages.root, byPath(filePath) { - return packages.packages.find(pkg => { - if (!path.relative(pkg.dir, filePath).startsWith('..')) { - return pkg; - } - }); + return packages.packages.find( + pkg => !path.relative(pkg.dir, filePath).startsWith('..'), + ); }, }; lastLoadAt = Date.now(); diff --git a/packages/eslint-plugin/lib/visitImports.js b/packages/eslint-plugin/lib/visitImports.js index ed16c26c7f..43401bf5f9 100644 --- a/packages/eslint-plugin/lib/visitImports.js +++ b/packages/eslint-plugin/lib/visitImports.js @@ -56,12 +56,17 @@ const getPackages = require('./getPackages'); /** * @callback ImportVisitor - * @param {import('eslint').Rule.Node} node + * @param {ConsideredNode} node * @param {LocalImport | InternalImport | ExternalImport | BuiltinImport} import */ /** - * @param {import('estree').ImportDeclaration | import('estree').ExportAllDeclaration | import('estree').ExportNamedDeclaration | import('estree').ImportExpression | import('estree').SimpleCallExpression} node + * @typedef ConsideredNode + * @type {import('estree').ImportDeclaration | import('estree').ExportAllDeclaration | import('estree').ExportNamedDeclaration | import('estree').ImportExpression | import('estree').SimpleCallExpression} + */ + +/** + * @param {ConsideredNode} node * @returns {undefined | {path: string, kind: 'type' | 'value'}} */ function getImportInfo(node) { @@ -103,6 +108,9 @@ module.exports = function visitImports(context, visitor) { return; } + /** + * @param {ConsideredNode} node + */ function visit(node) { const info = getImportInfo(node); if (!info) { diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index 4f60b712d9..85d7266eb8 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -37,7 +37,7 @@ const devModulePatterns = [ ]; function getExpectedDepType( - localPkg, + /** @type {any} */ localPkg, /** @type {string} */ impPath, /** @type {string} */ modulePath, ) { @@ -58,6 +58,7 @@ function getExpectedDepType( case 'react-router-dom': return 'peer'; } + break; case 'cli': case 'frontend': case 'backend': @@ -100,10 +101,14 @@ function findConflict(pkg, name, expectedType) { return { oldDepsField, depsField }; } + return undefined; } -function getAddFlagForConflict(conflict) { - switch (conflict?.depsField) { +/** + * @param {string} depsField + */ +function getAddFlagForDepsField(depsField) { + switch (depsField) { case depFields.dep: return ''; case depFields.dev: @@ -189,7 +194,7 @@ module.exports = { data: { ...conflict, packagePath, - addFlag: getAddFlagForConflict(conflict), + addFlag: getAddFlagForDepsField(conflict.depsField), packageName: imp.packageName, packageJsonPath: packageJsonPath, }, From e6d210947ece9b5fcaa90698f2b577936db3445c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 11:15:52 +0100 Subject: [PATCH 079/130] eslint-plugin: add tests for no-undeclared-imports + fixes Signed-off-by: Patrik Oldsberg --- packages/eslint-plugin/.eslintrc.js | 3 + .../rules/no-undeclared-imports.js | 2 +- .../monorepo/packages/bar/package.json | 7 +- .../monorepo/packages/foo/package.json | 7 +- .../src/no-undeclared-imports.test.ts | 194 ++++++++++++++++++ 5 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 packages/eslint-plugin/src/no-undeclared-imports.test.ts diff --git a/packages/eslint-plugin/.eslintrc.js b/packages/eslint-plugin/.eslintrc.js index 01d1b181d7..6b9de4544e 100644 --- a/packages/eslint-plugin/.eslintrc.js +++ b/packages/eslint-plugin/.eslintrc.js @@ -21,8 +21,11 @@ module.exports = { node: true, es2021: true, }, + parser: '@typescript-eslint/parser', parserOptions: { + ecmaVersion: 2018, sourceType: 'module', + lib: require('@backstage/cli/config/tsconfig.json').compilerOptions.lib, }, rules: { '@backstage/no-undeclared-imports': ['error'], diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index 85d7266eb8..42624beb5c 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -161,7 +161,7 @@ module.exports = { const modulePath = path.relative(localPkg.dir, filePath); const expectedType = getExpectedDepType( - localPkg, + localPkg.packageJson, imp.packageName, modulePath, ); diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json index 8331b20c43..c876e00e32 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json @@ -3,7 +3,10 @@ "backstage": { "role": "frontend-plugin" }, - "peerDependencies": { - "react": "*" + "dependencies": { + "react-router": "*" + }, + "devDependencies": { + "react-router-dom": "*" } } diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json index 66341563cd..8a14cad4d3 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -1,7 +1,12 @@ { "name": "@internal/foo", "dependencies": { - "@internal/bar": "1.0.0", + "@internal/bar": "1.0.0" + }, + "devDependencies": { "lodash": "*" + }, + "peerDependencies": { + "react": "*" } } diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts new file mode 100644 index 0000000000..c156558e8d --- /dev/null +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -0,0 +1,194 @@ +/* + * Copyright 2023 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 { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-undeclared-imports'; + +const RULE = 'no-undeclared-imports'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR_UNDECLARED = ( + name: string, + field: string, + path: string, + flag?: string, +) => ({ + message: `${name} must be declared in ${field} of ${path}/package.json, run 'yarn --cwd ${path} add${ + flag ? ` ${flag}` : '' + } ${name}' from the project root.`, +}); +const ERR_SWITCHED = ( + name: string, + old: string, + field: string, + path: string, +) => ({ + message: `${name} is declared in ${old}, but should be moved to ${field} in ${path}/package.json.`, +}); + +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + { + code: `import '@internal/foo'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + }, + { + code: `import '@internal/bar'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + }, + { + code: `import 'react'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + }, + { + code: `import '@internal/foo'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + }, + { + code: `import '@internal/bar'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + }, + { + code: `import 'lodash'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + }, + { + code: `import 'react'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + }, + { + // We're only able to validate literals + code: `require('lod' + 'ash')`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + }, + ], + invalid: [ + { + code: `import 'lodash'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + errors: [ + ERR_SWITCHED( + 'lodash', + 'devDependencies', + 'dependencies', + 'packages/foo', + ), + ], + }, + { + code: `import 'react-router'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_SWITCHED( + 'react-router', + 'dependencies', + 'peerDependencies', + 'packages/bar', + ), + ], + }, + { + code: `import 'react-router-dom'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_SWITCHED( + 'react-router-dom', + 'devDependencies', + 'peerDependencies', + 'packages/bar', + ), + ], + }, + { + code: `import 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import { debounce } from 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import * as _ from 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import _ from 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import('lodash')`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `require('lodash')`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + }, + { + code: `import 'lodash'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.test.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'devDependencies', 'packages/bar', '--dev'), + ], + }, + { + code: `import 'react'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('react', 'peerDependencies', 'packages/bar', '--peer'), + ], + }, + { + code: `import 'react'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.test.ts'), + errors: [ + ERR_UNDECLARED('react', 'peerDependencies', 'packages/bar', '--peer'), + ], + }, + { + code: `import 'react-dom'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + errors: [ERR_UNDECLARED('react-dom', 'dependencies', 'packages/foo')], + }, + { + code: `import 'react-dom'`, + filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + errors: [ + ERR_UNDECLARED('react-dom', 'devDependencies', 'packages/foo', '--dev'), + ], + }, + ], +}); From 2638dd4e7c9aa88ffcf987a33ff86465292094c4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 11:31:00 +0100 Subject: [PATCH 080/130] eslint-plugin: add tests for no-forbidden-package-imports Signed-off-by: Patrik Oldsberg --- .../monorepo/packages/bar/package.json | 5 + .../monorepo/packages/foo/package.json | 6 +- .../src/no-forbidden-package-imports.test.ts | 120 ++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin/src/no-forbidden-package-imports.test.ts diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json index c876e00e32..efef27047b 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json @@ -1,5 +1,10 @@ { "name": "@internal/bar", + "exports": { + ".": "./src/index.ts", + "./BarPage": "./src/components/Bar.tsx", + "./package.json": "./package.json" + }, "backstage": { "role": "frontend-plugin" }, diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json index 8a14cad4d3..563ec35120 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -8,5 +8,9 @@ }, "peerDependencies": { "react": "*" - } + }, + "files": [ + "dist", + "type-utils" + ] } diff --git a/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts new file mode 100644 index 0000000000..349b71319f --- /dev/null +++ b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts @@ -0,0 +1,120 @@ +/* + * Copyright 2023 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 { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-forbidden-package-imports'; + +const RULE = 'no-forbidden-package-imports'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR = (name: string, path: string) => ({ + message: `${name} does not export ${path}`, +}); + +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + { + code: `import '@internal/foo'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/foo/type-utils'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/foo/type-utils/anything'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/foo/package.json'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/bar'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/bar/package.json'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + { + code: `import '@internal/bar/BarPage'`, + filename: path.join(FIXTURE, 'index.ts'), + }, + ], + invalid: [ + { + code: `import '@internal/foo/FooPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/foo', 'FooPage')], + }, + { + code: `import '@internal/foo/dist'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/foo', 'dist')], + }, + { + code: `import '@internal/foo/dist/FooPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/foo', 'dist/FooPage')], + }, + { + code: `import '@internal/foo/src/FooPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/foo', 'src/FooPage')], + }, + { + code: `import '@internal/foo/src'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/foo', 'src')], + }, + { + code: `import '@internal/bar/OtherBarPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/bar', 'OtherBarPage')], + }, + { + code: `import '@internal/bar/dist'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/bar', 'dist')], + }, + { + code: `import '@internal/bar/dist/BarPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/bar', 'dist/BarPage')], + }, + { + code: `import '@internal/bar/src/BarPage'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/bar', 'src/BarPage')], + }, + { + code: `import '@internal/bar/src'`, + filename: path.join(FIXTURE, 'index.ts'), + errors: [ERR('@internal/bar', 'src')], + }, + ], +}); From 02f1316e57d709c7e8e458fa93828ef9ed1e96f2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 13:09:13 +0100 Subject: [PATCH 081/130] changeset: added changesets for eslint plugin fixes that affect the published package Signed-off-by: Patrik Oldsberg --- .changeset/grumpy-fireants-drop.md | 5 +++++ .changeset/wicked-beers-walk.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/grumpy-fireants-drop.md create mode 100644 .changeset/wicked-beers-walk.md diff --git a/.changeset/grumpy-fireants-drop.md b/.changeset/grumpy-fireants-drop.md new file mode 100644 index 0000000000..92b08663f4 --- /dev/null +++ b/.changeset/grumpy-fireants-drop.md @@ -0,0 +1,5 @@ +--- +'@backstage/codemods': patch +--- + +Moved `commander` to being a regular dependency. diff --git a/.changeset/wicked-beers-walk.md b/.changeset/wicked-beers-walk.md new file mode 100644 index 0000000000..160a171eb1 --- /dev/null +++ b/.changeset/wicked-beers-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Updated `packages/app/cypress/.eslintrc.json` to remove the unnecessary `import/no-extraneous-dependencies` rule. From ff63acf30a4bc94377b5c4f220deebe07af4d915 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 13:12:16 +0100 Subject: [PATCH 082/130] repo-tools: make api extraction ignore packages without a role Signed-off-by: Patrik Oldsberg --- .changeset/rare-berries-give.md | 5 +++++ .../repo-tools/src/commands/api-reports/api-extractor.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/rare-berries-give.md diff --git a/.changeset/rare-berries-give.md b/.changeset/rare-berries-give.md new file mode 100644 index 0000000000..39e360bf48 --- /dev/null +++ b/.changeset/rare-berries-give.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Packages without a declared `backstage.role` are now ignored. diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index b037f40be6..39df70c21a 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -1107,7 +1107,7 @@ export async function categorizePackageDirs(packageDirs: any[]) { }); const role = pkgJson?.backstage?.role; if (!role) { - throw new Error(`No backstage.role in ${dir}/package.json`); + return; // Ignore packages without roles } if (role === 'cli') { cliPackageDirs.push(dir); From dec4896baa9182120671195fe1fc81008deacbac Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 13:13:11 +0100 Subject: [PATCH 083/130] cli: add missing @backstage/eslint-plugin dependency Signed-off-by: Patrik Oldsberg --- packages/cli/package.json | 1 + yarn.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 359f16adfa..2f5292e3a0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -34,6 +34,7 @@ "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/eslint-plugin": "workspace:^", "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", diff --git a/yarn.lock b/yarn.lock index 2d250e9913..069b1125a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3660,6 +3660,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/eslint-plugin": "workspace:^" "@backstage/release-manifests": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" @@ -4094,7 +4095,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/eslint-plugin@workspace:packages/eslint-plugin": +"@backstage/eslint-plugin@workspace:^, @backstage/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@backstage/eslint-plugin@workspace:packages/eslint-plugin" dependencies: From baf6e4c96a90b54a52e8acc28e2dcaf706d63bd4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 4 Feb 2023 13:57:18 +0100 Subject: [PATCH 084/130] backend-test-utils: removed unnecessary @backstage/cli dependency Signed-off-by: Patrik Oldsberg --- .changeset/chilled-sheep-collect.md | 5 +++++ packages/backend-test-utils/package.json | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/chilled-sheep-collect.md diff --git a/.changeset/chilled-sheep-collect.md b/.changeset/chilled-sheep-collect.md new file mode 100644 index 0000000000..b944685e16 --- /dev/null +++ b/.changeset/chilled-sheep-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Removed unnecessary `@backstage/cli` dependency. diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index a01dbaf0fa..d1f0a86d43 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -37,7 +37,6 @@ "@backstage/backend-app-api": "workspace:^", "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", - "@backstage/cli": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/types": "workspace:^", From 16acf34bf2b7c14151c91afb96faf4428704e530 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sat, 4 Feb 2023 13:21:37 -0500 Subject: [PATCH 085/130] improve Vault plugin catalog entry language * correct grammar * clarify that the plugin deals with HashiCorp Vault (and not another Vault, such as Ansible Vault) Signed-off-by: Mike Ball --- microsite/data/plugins/vault.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/vault.yaml b/microsite/data/plugins/vault.yaml index 822f5dcc90..bb8f799ba7 100644 --- a/microsite/data/plugins/vault.yaml +++ b/microsite/data/plugins/vault.yaml @@ -3,7 +3,7 @@ title: Vault author: Spread Group authorUrl: https://github.com/ivangonzalezacuna category: Vault -description: Visualize a list secrets stored in your vault instance. +description: Visualize a list of the secrets stored in your HashiCorp Vault instance. documentation: https://github.com/backstage/backstage/tree/master/plugins/vault iconUrl: img/vault.png npmPackageName: '@backstage/plugin-vault' From 9d9cdea1173223a6630e96d93dbc67fdf83bd119 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Jan 2023 15:54:30 +0100 Subject: [PATCH 086/130] create backend-dev-utils with cli ipc client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- .changeset/green-adults-retire.md | 5 + packages/backend-dev-utils/.eslintrc.js | 1 + packages/backend-dev-utils/README.md | 5 + packages/backend-dev-utils/package.json | 36 ++++++ packages/backend-dev-utils/src/index.ts | 21 ++++ packages/backend-dev-utils/src/ipcClient.ts | 120 +++++++++++++++++++ packages/backend-dev-utils/src/setupTests.ts | 16 +++ yarn.lock | 8 ++ 8 files changed, 212 insertions(+) create mode 100644 .changeset/green-adults-retire.md create mode 100644 packages/backend-dev-utils/.eslintrc.js create mode 100644 packages/backend-dev-utils/README.md create mode 100644 packages/backend-dev-utils/package.json create mode 100644 packages/backend-dev-utils/src/index.ts create mode 100644 packages/backend-dev-utils/src/ipcClient.ts create mode 100644 packages/backend-dev-utils/src/setupTests.ts diff --git a/.changeset/green-adults-retire.md b/.changeset/green-adults-retire.md new file mode 100644 index 0000000000..c13e8d664f --- /dev/null +++ b/.changeset/green-adults-retire.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dev-utils': minor +--- + +Introduced a new package for backend development utilities. Similar to how `@backstage/dev-utils` is used in the frontend. diff --git a/packages/backend-dev-utils/.eslintrc.js b/packages/backend-dev-utils/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/packages/backend-dev-utils/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/backend-dev-utils/README.md b/packages/backend-dev-utils/README.md new file mode 100644 index 0000000000..8acce94cf0 --- /dev/null +++ b/packages/backend-dev-utils/README.md @@ -0,0 +1,5 @@ +# backend-dev-utils + +Welcome to the backend-dev-utils package! + +_This package is experimental._ diff --git a/packages/backend-dev-utils/package.json b/packages/backend-dev-utils/package.json new file mode 100644 index 0000000000..735185e37f --- /dev/null +++ b/packages/backend-dev-utils/package.json @@ -0,0 +1,36 @@ +{ + "name": "@backstage/backend-dev-utils", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "node-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend-dev-utils" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/packages/backend-dev-utils/src/index.ts b/packages/backend-dev-utils/src/index.ts new file mode 100644 index 0000000000..bddecad73d --- /dev/null +++ b/packages/backend-dev-utils/src/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 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. + */ + +/** + * Local development helpers for backend. + * + * @packageDocumentation + */ diff --git a/packages/backend-dev-utils/src/ipcClient.ts b/packages/backend-dev-utils/src/ipcClient.ts new file mode 100644 index 0000000000..d3fa184a3f --- /dev/null +++ b/packages/backend-dev-utils/src/ipcClient.ts @@ -0,0 +1,120 @@ +/* + * Copyright 2023 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. + */ + +type SendMessage = Exclude; + +interface Request { + id: number; + method: string; + body: unknown; + type: string; +} + +type Response = + | { + type: string; + id: number; + body: unknown; + } + | { + type: string; + id: number; + error: Error; + }; + +const requestType = '@backstage/cli/channel/request'; +const responseType = '@backstage/cli/channel/response'; + +/** + * The client side of an IPC communication channel. + * + * @internal + */ +export class BackstageIpcClient { + #messageId = 0; + #sendMessage: SendMessage; + + /** + * Creates a new client if we're in a child process with IPC and BACKSTAGE_CLI_CHANNEL is set. + */ + static create(): BackstageIpcClient | undefined { + const sendMessage = process.send?.bind(process); + return sendMessage && process.env.BACKSTAGE_CLI_CHANNEL + ? new BackstageIpcClient(sendMessage) + : undefined; + } + + constructor(sendMessage: SendMessage) { + this.#sendMessage = sendMessage; + } + + /** + * Send a request to the parent process and wait for a response. + */ + async request( + method: string, + body: TRequestBody, + ): Promise { + return new Promise((resolve, reject) => { + const id = this.#messageId++; + + const request: Request = { + type: requestType, + id, + method, + body, + }; + + this.#sendMessage(request, (e: Error) => { + if (e) { + reject(e); + return; + } + + const timeout = setTimeout(() => { + reject(new Error('IPC request timed out')); + }, 1000); + timeout.unref(); + + const messageHandler = (response: Response) => { + if (response?.type !== responseType) { + return; + } + if (response.id !== id) { + return; + } + + if ('error' in response) { + const error = new Error(response.error.message); + if (response.error.name) { + error.name = response.error.name; + } + reject(error); + } else { + resolve(response.body as TResponseBody); + } + + clearTimeout(timeout); + process.removeListener('message', messageHandler); + }; + + process.addListener('message', messageHandler as () => void); + }); + }); + } +} + +export const ipcClient = BackstageIpcClient.create(); diff --git a/packages/backend-dev-utils/src/setupTests.ts b/packages/backend-dev-utils/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/packages/backend-dev-utils/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 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 {}; diff --git a/yarn.lock b/yarn.lock index 3d809a40c8..ecd381c8f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3519,6 +3519,14 @@ __metadata: languageName: unknown linkType: soft +"@backstage/backend-dev-utils@workspace:packages/backend-dev-utils": + version: 0.0.0-use.local + resolution: "@backstage/backend-dev-utils@workspace:packages/backend-dev-utils" + dependencies: + "@backstage/cli": "workspace:^" + languageName: unknown + linkType: soft + "@backstage/backend-plugin-api@workspace:^, @backstage/backend-plugin-api@workspace:packages/backend-plugin-api": version: 0.0.0-use.local resolution: "@backstage/backend-plugin-api@workspace:packages/backend-plugin-api" From a70806c3b09b851bc5785cc32700cb5b36f58f39 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Jan 2023 12:08:55 +0100 Subject: [PATCH 087/130] backend-dev-utils: add DevDataStore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- packages/backend-dev-utils/api-report.md | 20 +++ .../src/DevDataStore.test.ts | 121 ++++++++++++++++++ .../backend-dev-utils/src/DevDataStore.ts | 99 ++++++++++++++ packages/backend-dev-utils/src/index.ts | 2 + 4 files changed, 242 insertions(+) create mode 100644 packages/backend-dev-utils/api-report.md create mode 100644 packages/backend-dev-utils/src/DevDataStore.test.ts create mode 100644 packages/backend-dev-utils/src/DevDataStore.ts diff --git a/packages/backend-dev-utils/api-report.md b/packages/backend-dev-utils/api-report.md new file mode 100644 index 0000000000..216311ccd8 --- /dev/null +++ b/packages/backend-dev-utils/api-report.md @@ -0,0 +1,20 @@ +## API Report File for "@backstage/backend-dev-utils" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// @public +export class DevDataStore { + static get(): DevDataStore | undefined; + load(key: string): Promise<{ + loaded: boolean; + data: T; + }>; + save( + key: string, + data: T, + ): Promise<{ + saved: boolean; + }>; +} +``` diff --git a/packages/backend-dev-utils/src/DevDataStore.test.ts b/packages/backend-dev-utils/src/DevDataStore.test.ts new file mode 100644 index 0000000000..2bb3f221ae --- /dev/null +++ b/packages/backend-dev-utils/src/DevDataStore.test.ts @@ -0,0 +1,121 @@ +/* + * Copyright 2023 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 { Serializable, spawn } from 'child_process'; +import { DevDataStore } from './DevDataStore'; +import { BackstageIpcClient } from './ipcClient'; + +function applyIpcTransform(value: Serializable): Promise { + const child = spawn( + 'node', + [ + '--eval', + ` + const listener = msg => { + process.send(msg); + process.removeListener('message', listener); + } + process.addListener('message', listener); + `, + ], + { + stdio: ['inherit', 'inherit', 'inherit', 'ipc'], + serialization: 'advanced', + }, + ); + + return new Promise(resolve => { + child.addListener('message', resolve); + child.send(value); + }); +} + +class MockIpcClient implements Pick { + #data = new Map(); + + async request(method: string, body: any): Promise { + if (method === 'DevDataStore.save') { + this.#data.set(body.key, body.data); + return { saved: true }; + } else if (method === 'DevDataStore.load') { + if (!this.#data.has(body.key)) { + return { loaded: false, data: undefined }; + } + const data = this.#data.get(body.key)!; + return { loaded: true, data: await applyIpcTransform(data) }; + } + throw new Error('Unknown message'); + } +} + +describe('DevDataStore', () => { + it('should save and load data', async () => { + const store = DevDataStore.forTest(new MockIpcClient()); + + await expect(store.save('test', { foo: 'bar' })).resolves.toEqual({ + saved: true, + }); + await expect(store.load('test')).resolves.toEqual({ + loaded: true, + data: { foo: 'bar' }, + }); + }); + + it('should save and load buffers', async () => { + const store = DevDataStore.forTest(new MockIpcClient()); + + await expect( + store.save('test', Buffer.from('abc', 'ascii')), + ).resolves.toEqual({ + saved: true, + }); + await expect(store.load('test')).resolves.toEqual({ + loaded: true, + data: Buffer.from('abc', 'ascii'), + }); + }); + + it('should save and load array buffers', async () => { + const store = DevDataStore.forTest(new MockIpcClient()); + + await expect( + store.save('test', new Uint8Array(Buffer.from('abc', 'ascii'))), + ).resolves.toEqual({ + saved: true, + }); + await expect(store.load('test')).resolves.toEqual({ + loaded: true, + data: new Uint8Array(Buffer.from('abc', 'ascii')), + }); + }); + + it('should save and load buffers nested in objects', async () => { + const store = DevDataStore.forTest(new MockIpcClient()); + + await expect( + store.save('test', { + x: Buffer.from('x', 'ascii'), + y: [Buffer.from('y', 'ascii')], + }), + ).resolves.toEqual({ + saved: true, + }); + await expect(store.load('test')).resolves.toEqual({ + loaded: true, + data: { x: Buffer.from('x', 'ascii'), y: [Buffer.from('y', 'ascii')] }, + }); + }); +}); diff --git a/packages/backend-dev-utils/src/DevDataStore.ts b/packages/backend-dev-utils/src/DevDataStore.ts new file mode 100644 index 0000000000..4069ab1bdb --- /dev/null +++ b/packages/backend-dev-utils/src/DevDataStore.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2023 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 { BackstageIpcClient, ipcClient } from './ipcClient'; + +interface SaveRequest { + key: string; + data: unknown; +} + +interface SaveResponse { + saved: boolean; +} + +interface LoadRequest { + key: string; +} + +interface LoadResponse { + loaded: boolean; + data: unknown; +} + +/** + * A data store that can be used to store temporary data during development. + * + * @public + */ +export class DevDataStore { + static #instance?: DevDataStore; + + /** + * Tries to acquire a DevDataStore instance. This will only succeed when the backend + * process is being run through the `@backstage/cli` in development mode. + * + * @returns A DevDataStore instance, or undefined if not available. + */ + static get(): DevDataStore | undefined { + if (ipcClient) { + if (!this.#instance) { + this.#instance = new DevDataStore(ipcClient); + } + return this.#instance; + } + return undefined; + } + + /** @internal */ + static forTest(client: Pick): DevDataStore { + return new DevDataStore(client as BackstageIpcClient); + } + + #client: BackstageIpcClient; + + private constructor(client: BackstageIpcClient) { + this.#client = client; + } + + /** + * Save data to the data store. + * + * @param key - The key used to identify the data. + * @param data - The data to save. The data will be serialized using advanced IPC serialization. + * @returns A promise that resolves to a result object that indicates whether the data was saved. + */ + async save(key: string, data: T): Promise<{ saved: boolean }> { + return this.#client.request( + 'DevDataStore.save', + { key, data }, + ); + } + + /** + * Loads data from the data store. + * + * @param key - The key used to identify the data. + * @returns A promise that resolves to a result object that indicates whether the data was loaded, as well as the data. + */ + async load(key: string): Promise<{ loaded: boolean; data: T }> { + const result = await this.#client.request( + 'DevDataStore.load', + { key }, + ); + return result as { loaded: boolean; data: T }; + } +} diff --git a/packages/backend-dev-utils/src/index.ts b/packages/backend-dev-utils/src/index.ts index bddecad73d..9b707db029 100644 --- a/packages/backend-dev-utils/src/index.ts +++ b/packages/backend-dev-utils/src/index.ts @@ -19,3 +19,5 @@ * * @packageDocumentation */ + +export { DevDataStore } from './DevDataStore'; From 90616df9a8784703dabc093337343ceab5d473d0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Jan 2023 16:28:40 +0100 Subject: [PATCH 088/130] cli: add experimental backend start Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- .changeset/weak-radios-sing.md | 5 + packages/cli/package.json | 3 + .../cli/src/commands/start/startBackend.ts | 34 +++-- .../cli/src/lib/experimental/ChannelServer.ts | 102 +++++++++++++ .../src/lib/experimental/ServerDataStore.ts | 78 ++++++++++ .../experimental/startBackendExperimental.ts | 138 ++++++++++++++++++ packages/cli/src/types.d.ts | 2 + yarn.lock | 43 +++++- 8 files changed, 392 insertions(+), 13 deletions(-) create mode 100644 .changeset/weak-radios-sing.md create mode 100644 packages/cli/src/lib/experimental/ChannelServer.ts create mode 100644 packages/cli/src/lib/experimental/ServerDataStore.ts create mode 100644 packages/cli/src/lib/experimental/startBackendExperimental.ts diff --git a/.changeset/weak-radios-sing.md b/.changeset/weak-radios-sing.md new file mode 100644 index 0000000000..dff287b565 --- /dev/null +++ b/.changeset/weak-radios-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added an experimental mode for the `package start` command for backend packages. Enabled by setting `EXPERIMENTAL_BACKEND_START`. diff --git a/packages/cli/package.json b/packages/cli/package.json index 909ac8460c..e6b9b7a97e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,6 +36,7 @@ "@backstage/errors": "workspace:^", "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", + "@esbuild-kit/cjs-loader": "^2.4.1", "@manypkg/get-packages": "^1.1.3", "@octokit/request": "^6.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -65,6 +66,7 @@ "chalk": "^4.0.0", "chokidar": "^3.3.1", "commander": "^9.1.0", + "cross-spawn": "^7.0.3", "css-loader": "^6.5.1", "diff": "^5.0.0", "esbuild": "^0.17.0", @@ -136,6 +138,7 @@ "@backstage/dev-utils": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/theme": "workspace:^", + "@types/cross-spawn": "^6.0.2", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", diff --git a/packages/cli/src/commands/start/startBackend.ts b/packages/cli/src/commands/start/startBackend.ts index 61ada7a291..3e6a13be96 100644 --- a/packages/cli/src/commands/start/startBackend.ts +++ b/packages/cli/src/commands/start/startBackend.ts @@ -17,6 +17,7 @@ import fs from 'fs-extra'; import { paths } from '../../lib/paths'; import { serveBackend } from '../../lib/bundler'; +import { startBackendExperimental } from '../../lib/experimental/startBackendExperimental'; interface StartBackendOptions { checksEnabled: boolean; @@ -25,17 +26,28 @@ interface StartBackendOptions { } export async function startBackend(options: StartBackendOptions) { - // Cleaning dist/ before we start the dev process helps work around an issue - // where we end up with the entrypoint executing multiple times, causing - // a port bind conflict among other things. - await fs.remove(paths.resolveTarget('dist')); + if (process.env.EXPERIMENTAL_BACKEND_START) { + const waitForExit = await startBackendExperimental({ + entry: 'src/index', + checksEnabled: false, // not supported + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + }); - const waitForExit = await serveBackend({ - entry: 'src/index', - checksEnabled: options.checksEnabled, - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - }); + await waitForExit(); + } else { + // Cleaning dist/ before we start the dev process helps work around an issue + // where we end up with the entrypoint executing multiple times, causing + // a port bind conflict among other things. + await fs.remove(paths.resolveTarget('dist')); - await waitForExit(); + const waitForExit = await serveBackend({ + entry: 'src/index', + checksEnabled: options.checksEnabled, + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + }); + + await waitForExit(); + } } diff --git a/packages/cli/src/lib/experimental/ChannelServer.ts b/packages/cli/src/lib/experimental/ChannelServer.ts new file mode 100644 index 0000000000..db949f95b8 --- /dev/null +++ b/packages/cli/src/lib/experimental/ChannelServer.ts @@ -0,0 +1,102 @@ +/* + * Copyright 2023 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 { serializeError } from '@backstage/errors'; +import { ChildProcess } from 'child_process'; + +interface RequestMeta { + generation: number; +} + +type MethodHandler = ( + req: TRequest, + meta: RequestMeta, +) => Promise; + +interface Request { + id: number; + method: string; + body: unknown; + type: string; +} + +const requestType = '@backstage/cli/channel/request'; +const responseType = '@backstage/cli/channel/response'; + +export class ChannelServer { + #generation = 1; + #methods = new Map>(); + + addChild(child: ChildProcess) { + const generation = this.#generation++; + const sendMessage = child.send?.bind(child); + if (!sendMessage) { + return; + } + + const messageListener = (request: Request) => { + if (request.type !== requestType) { + return; + } + + const handler = this.#methods.get(request.method); + if (!handler) { + sendMessage({ + type: responseType, + id: request.id, + error: { + name: 'NotFoundError', + message: `No handler registered for method ${request.method}`, + }, + }); + return; + } + + Promise.resolve() + .then(() => handler(request.body, { generation })) + .then(response => + sendMessage({ + type: responseType, + id: request.id, + body: response, + }), + ) + .catch(error => + sendMessage({ + type: responseType, + id: request.id, + error: serializeError(error), + }), + ); + }; + + child.addListener('message', messageListener as (req: unknown) => void); + + child.addListener('exit', () => { + child.removeListener('message', messageListener); + }); + } + + registerMethod( + method: string, + handler: MethodHandler, + ) { + if (this.#methods.has(method)) { + throw new Error(`A handler is already registered for method ${method}`); + } + this.#methods.set(method, handler); + } +} diff --git a/packages/cli/src/lib/experimental/ServerDataStore.ts b/packages/cli/src/lib/experimental/ServerDataStore.ts new file mode 100644 index 0000000000..29ac331de5 --- /dev/null +++ b/packages/cli/src/lib/experimental/ServerDataStore.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2023 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 { ChannelServer } from './ChannelServer'; + +interface StorageItem { + generation: number; + data: unknown; +} + +interface SaveRequest { + key: string; + data: unknown; +} + +interface SaveResponse { + saved: boolean; +} + +interface LoadRequest { + key: string; +} + +interface LoadResponse { + loaded: boolean; + data: unknown; +} + +export class ServerDataStore { + static bind(server: ChannelServer): void { + const store = new Map(); + + server.registerMethod( + 'DevDataStore.save', + async (request, { generation }) => { + const { key, data } = request; + if (!key) { + throw new Error('Key is required in DevDataStore.save'); + } + + const item = store.get(key); + + if (!item) { + store.set(key, { generation, data }); + return { saved: true }; + } + + if (item.generation > generation) { + return { saved: false }; + } + + store.set(key, { generation, data }); + return { saved: true }; + }, + ); + + server.registerMethod( + 'DevDataStore.load', + async request => { + const item = store.get(request.key); + return { loaded: Boolean(item), data: item?.data }; + }, + ); + } +} diff --git a/packages/cli/src/lib/experimental/startBackendExperimental.ts b/packages/cli/src/lib/experimental/startBackendExperimental.ts new file mode 100644 index 0000000000..94bf066925 --- /dev/null +++ b/packages/cli/src/lib/experimental/startBackendExperimental.ts @@ -0,0 +1,138 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackendServeOptions } from '../bundler/types'; +import type { ChildProcess } from 'child_process'; +import { fileURLToPath } from 'url'; +import { isAbsolute as isAbsolutePath } from 'path'; +import { FSWatcher, watch } from 'chokidar'; +import { ChannelServer } from './ChannelServer'; +import { ServerDataStore } from './ServerDataStore'; +import debounce from 'lodash/debounce'; +import spawn from 'cross-spawn'; +import { paths } from '../paths'; + +const loaderArgs = ['--require', require.resolve('@esbuild-kit/cjs-loader')]; + +export async function startBackendExperimental(options: BackendServeOptions) { + const envEnv = process.env as { NODE_ENV: string }; + if (!envEnv.NODE_ENV) { + envEnv.NODE_ENV = 'development'; + } + + // Set up the parent IPC server and bind the available services + const server = new ChannelServer(); + ServerDataStore.bind(server); + + let exiting = false; + let child: ChildProcess | undefined; + let watcher: FSWatcher | undefined = undefined; + let shutdownPromise: Promise | undefined = undefined; + + const restart = debounce(async () => { + // If a re-trigger happens during an existing shutdown, we just ignore it + if (shutdownPromise) { + return; + } + + if (child && !child.killed && child.exitCode === null) { + // We always wait for the existing process to exit, to make sure we don't get IPC conflicts + shutdownPromise = new Promise(resolve => child!.once('exit', resolve)); + child.kill(); + await shutdownPromise; + shutdownPromise = undefined; + } + + // We've received a shutdown signal + if (exiting) { + return; + } + + const optionArgs = new Array(); + if (options.inspectEnabled) { + optionArgs.push('--inspect'); + } else if (options.inspectBrkEnabled) { + optionArgs.push('--inspect-brk'); + } + + const userArgs = process.argv + .slice(['node', 'backstage-cli', 'package', 'start'].length) + .filter(arg => !optionArgs.includes(arg)); + + child = spawn( + process.execPath, + [...loaderArgs, ...optionArgs, options.entry, ...userArgs], + { + stdio: ['inherit', 'inherit', 'inherit', 'ipc'], + env: { + ...process.env, + BACKSTAGE_CLI_CHANNEL: '1', + ESBK_TSCONFIG_PATH: paths.resolveTargetRoot('tsconfig.json'), + }, + serialization: 'advanced', + }, + ); + + server.addChild(child); + + // This captures messages sent by @esbuild-kit/cjs-loader + child.on('message', (data: { type?: string } | null) => { + if (typeof data === 'object' && data?.type === 'dependency') { + let path = (data as { path: string }).path; + if (path.startsWith('file:')) { + path = fileURLToPath(path); + } + + if (isAbsolutePath(path)) { + watcher?.add(path); + } + } + }); + }, 100); + + restart(); + + watcher = watch([paths.targetDir], { + cwd: process.cwd(), + ignored: ['**/.*/**', '**/node_modules/**'], + ignoreInitial: true, + ignorePermissionErrors: true, + }).on('all', restart); + + // Trigger restart on hitting enter in the terminal + process.stdin.on('data', restart); + + const exitPromise = new Promise(resolveExitPromise => { + async function handleSignal(signal: NodeJS.Signals) { + exiting = true; + + // Forward signals to child and wait for it to exit if still running + if (child && child.exitCode === null) { + await new Promise(resolve => { + child!.on('close', resolve); + child!.kill(signal); + }); + } + + resolveExitPromise(); + } + + process.once('SIGINT', handleSignal); + process.once('SIGTERM', handleSignal); + }); + + return () => exitPromise; +} diff --git a/packages/cli/src/types.d.ts b/packages/cli/src/types.d.ts index fedbe61f02..1222b2b4b8 100644 --- a/packages/cli/src/types.d.ts +++ b/packages/cli/src/types.d.ts @@ -258,3 +258,5 @@ declare module 'webpack-node-externals' { } } } + +declare module '@esbuild-kit/cjs-loader' {} diff --git a/yarn.lock b/yarn.lock index ecd381c8f8..7143443674 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3672,6 +3672,7 @@ __metadata: "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" + "@esbuild-kit/cjs-loader": ^2.4.1 "@manypkg/get-packages": ^1.1.3 "@octokit/request": ^6.0.0 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.7 @@ -3690,6 +3691,7 @@ __metadata: "@swc/core": ^1.3.9 "@swc/helpers": ^0.4.7 "@swc/jest": ^0.2.22 + "@types/cross-spawn": ^6.0.2 "@types/diff": ^5.0.0 "@types/express": ^4.17.6 "@types/fs-extra": ^9.0.1 @@ -3717,6 +3719,7 @@ __metadata: chalk: ^4.0.0 chokidar: ^3.3.1 commander: ^9.1.0 + cross-spawn: ^7.0.3 css-loader: ^6.5.1 del: ^6.0.0 diff: ^5.0.0 @@ -9196,6 +9199,26 @@ __metadata: languageName: node linkType: hard +"@esbuild-kit/cjs-loader@npm:^2.4.1": + version: 2.4.1 + resolution: "@esbuild-kit/cjs-loader@npm:2.4.1" + dependencies: + "@esbuild-kit/core-utils": ^3.0.0 + get-tsconfig: ^4.2.0 + checksum: a516065907be0ead76ac2199ccb08ff92659ba5e2edb4bb8772b6a63afe4faed7eb45a3b4d87266a68c7c135c3dba971cd087bc6f16c382356e835c7dd3440f5 + languageName: node + linkType: hard + +"@esbuild-kit/core-utils@npm:^3.0.0": + version: 3.0.0 + resolution: "@esbuild-kit/core-utils@npm:3.0.0" + dependencies: + esbuild: ~0.15.10 + source-map-support: ^0.5.21 + checksum: 0e89ec718e2211bf95c48a8085aaef88e8e416f42abd1c62d488d5458eecd3fbc144179a0c5570ad36fa7e2d3bbc411f8d3fb28802c37ced2154dc2c6ded9dfe + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.17.5": version: 0.17.5 resolution: "@esbuild/android-arm64@npm:0.17.5" @@ -13912,6 +13935,15 @@ __metadata: languageName: node linkType: hard +"@types/cross-spawn@npm:^6.0.2": + version: 6.0.2 + resolution: "@types/cross-spawn@npm:6.0.2" + dependencies: + "@types/node": "*" + checksum: fa9edd32178878cab3ea8d6d0260639e0fe4860ddb3887b8de53d6e8036e154fc5f313c653f690975aa25025aea8beb83fb0870b931bf8d9202c3ac530a24c9d + languageName: node + linkType: hard + "@types/d3-color@npm:*": version: 3.0.2 resolution: "@types/d3-color@npm:3.0.2" @@ -21335,7 +21367,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.15.6": +"esbuild@npm:^0.15.6, esbuild@npm:~0.15.10": version: 0.15.18 resolution: "esbuild@npm:0.15.18" dependencies: @@ -23493,6 +23525,13 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.2.0": + version: 4.3.0 + resolution: "get-tsconfig@npm:4.3.0" + checksum: 2597aab99aa3a24db209e192a3e5874ac47fc5abc71703ee26346e0c5816cb346ca09fc813c739db5862d3a2905d89aeca1b0cbc46c2b272398d672309aaf414 + languageName: node + linkType: hard + "getopts@npm:2.3.0": version: 2.3.0 resolution: "getopts@npm:2.3.0" @@ -35146,7 +35185,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.16, source-map-support@npm:~0.5.20": +"source-map-support@npm:^0.5.16, source-map-support@npm:^0.5.21, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: From ab2251564746e53a10759267dacdf83bb7d6769d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Jan 2023 17:58:58 +0100 Subject: [PATCH 089/130] backend-app-api: move signal handlers to backend initializer + exit Signed-off-by: Patrik Oldsberg --- .changeset/red-kiwis-compare.md | 5 +++++ .../rootLifecycle/rootLifecycleFactory.ts | 5 +---- .../backend-app-api/src/wiring/BackendInitializer.ts | 12 ++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 .changeset/red-kiwis-compare.md diff --git a/.changeset/red-kiwis-compare.md b/.changeset/red-kiwis-compare.md new file mode 100644 index 0000000000..6045367105 --- /dev/null +++ b/.changeset/red-kiwis-compare.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +The shutdown signal handlers are now installed as part of the backend instance rather than the lifecycle service, and explicitly cause the process to exit. diff --git a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts index 71ab76ae92..ebff53e9a6 100644 --- a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts +++ b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts @@ -22,11 +22,8 @@ import { LoggerService, } from '@backstage/backend-plugin-api'; -const CALLBACKS = ['SIGTERM', 'SIGINT', 'beforeExit']; export class BackendLifecycleImpl implements RootLifecycleService { - constructor(private readonly logger: LoggerService) { - CALLBACKS.map(signal => process.on(signal, () => this.shutdown())); - } + constructor(private readonly logger: LoggerService) {} #isCalled = false; #shutdownTasks: Array = []; diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index ce17398a79..d1139615f1 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -87,6 +87,18 @@ export class BackendInitializer { } this.#started = true; + for (const event of ['SIGTERM', 'SIGINT', 'beforeExit']) { + process.on(event, async () => { + try { + await this.stop(); + process.exit(0); + } catch (error) { + console.error(error); + process.exit(1); + } + }); + } + // Initialize all root scoped services for (const ref of this.#serviceHolder.getServiceRefs()) { if (ref.scope === 'root') { From f60cca9da1defe5786a0f16bb17fac2502fca043 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 1 Feb 2023 10:57:42 +0100 Subject: [PATCH 090/130] backend-common: add support for restoring database state via dev store Signed-off-by: Patrik Oldsberg --- .changeset/long-chefs-burn.md | 5 ++ .changeset/old-ways-know.md | 5 ++ .../database/databaseFactory.ts | 10 +++- packages/backend-common/api-report.md | 14 ++++- packages/backend-common/package.json | 1 + .../src/database/DatabaseManager.ts | 15 +++-- .../backend-common/src/database/connection.ts | 5 +- .../src/database/connectors/sqlite3.ts | 55 +++++++++++++++---- packages/backend-common/src/database/index.ts | 5 +- packages/backend-common/src/database/types.ts | 20 ++++++- yarn.lock | 3 +- 11 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 .changeset/long-chefs-burn.md create mode 100644 .changeset/old-ways-know.md diff --git a/.changeset/long-chefs-burn.md b/.changeset/long-chefs-burn.md new file mode 100644 index 0000000000..03a7b76db9 --- /dev/null +++ b/.changeset/long-chefs-burn.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Updated database factory to pass service deps required for restoring database state during development. diff --git a/.changeset/old-ways-know.md b/.changeset/old-ways-know.md new file mode 100644 index 0000000000..60a1a923e8 --- /dev/null +++ b/.changeset/old-ways-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +The `DatabaseManager.forPlugin` method now accepts additional service dependencies. There is no need to update existing code to pass these dependencies. diff --git a/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts b/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts index 8baf84cb69..4d95f863db 100644 --- a/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts +++ b/packages/backend-app-api/src/services/implementations/database/databaseFactory.ts @@ -26,7 +26,8 @@ export const databaseFactory = createServiceFactory({ service: coreServices.database, deps: { config: coreServices.config, - plugin: coreServices.pluginMetadata, + lifecycle: coreServices.lifecycle, + pluginMetadata: coreServices.pluginMetadata, }, async createRootContext({ config }) { return config.getOptional('backend.database') @@ -39,7 +40,10 @@ export const databaseFactory = createServiceFactory({ }), ); }, - async factory({ plugin }, databaseManager) { - return databaseManager.forPlugin(plugin.getId()); + async factory({ pluginMetadata, lifecycle }, databaseManager) { + return databaseManager.forPlugin(pluginMetadata.getId(), { + pluginMetadata, + lifecycle, + }); }, }); diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index b03d6ec866..8b6311f8b0 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -32,6 +32,7 @@ import { IdentityService } from '@backstage/backend-plugin-api'; import { isChildPath } from '@backstage/cli-common'; import { Knex } from 'knex'; import { KubeConfig } from '@kubernetes/client-node'; +import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoadConfigOptionsRemote } from '@backstage/config-loader'; import { Logger } from 'winston'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -40,6 +41,7 @@ import { PermissionsService } from '@backstage/backend-plugin-api'; import { CacheService as PluginCacheManager } from '@backstage/backend-plugin-api'; import { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; import { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api'; +import { PluginMetadataService } from '@backstage/backend-plugin-api'; import { PushResult } from 'isomorphic-git'; import { Readable } from 'stream'; import { ReadCommitResult } from 'isomorphic-git'; @@ -232,6 +234,7 @@ export class Contexts { export function createDatabaseClient( dbConfig: Config, overrides?: Partial, + deps?: PluginDatabaseDependencies, ): Knex; // @public @@ -252,7 +255,10 @@ export function createStatusCheckRouter(options: { // @public export class DatabaseManager { - forPlugin(pluginId: string): PluginDatabaseManager; + forPlugin( + pluginId: string, + deps?: PluginDatabaseDependencies, + ): PluginDatabaseManager; static fromConfig( config: Config, options?: DatabaseManagerOptions, @@ -571,6 +577,12 @@ export function notFoundHandler(): RequestHandler; export { PluginCacheManager }; +// @public +export type PluginDatabaseDependencies = { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; +}; + export { PluginDatabaseManager }; export { PluginEndpointDiscovery }; diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index c950950680..9f1069f322 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@backstage/backend-app-api": "workspace:^", + "@backstage/backend-dev-utils": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/cli-common": "workspace:^", "@backstage/config": "workspace:^", diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 5a497fff29..d8205faec1 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -27,7 +27,7 @@ import { ensureSchemaExists, normalizeConnection, } from './connection'; -import { PluginDatabaseManager } from './types'; +import { PluginDatabaseDependencies, PluginDatabaseManager } from './types'; import path from 'path'; import { LoggerService } from '@backstage/backend-plugin-api'; import { stringifyError } from '@backstage/errors'; @@ -94,12 +94,15 @@ export class DatabaseManager { * should be unique as they are used to look up database config overrides under * `backend.database.plugin`. */ - forPlugin(pluginId: string): PluginDatabaseManager { + forPlugin( + pluginId: string, + deps?: PluginDatabaseDependencies, + ): PluginDatabaseManager { const _this = this; return { getClient(): Promise { - return _this.getDatabase(pluginId); + return _this.getDatabase(pluginId, deps); }, migrations: { skip: false, @@ -307,7 +310,10 @@ export class DatabaseManager { * @returns Promise which resolves to a scoped Knex database client for a * plugin */ - private async getDatabase(pluginId: string): Promise { + private async getDatabase( + pluginId: string, + deps?: PluginDatabaseDependencies, + ): Promise { if (this.databaseCache.has(pluginId)) { return this.databaseCache.get(pluginId)!; } @@ -351,6 +357,7 @@ export class DatabaseManager { const client = createDatabaseClient( pluginConfig, databaseClientOverrides, + deps, ); this.startKeepaliveLoop(pluginId, client); return client; diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index 5f487886b4..054ab7cfa8 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -19,7 +19,7 @@ import { JsonObject } from '@backstage/types'; import { InputError } from '@backstage/errors'; import knexFactory, { Knex } from 'knex'; import { mergeDatabaseConfig } from './config'; -import { DatabaseConnector } from './types'; +import { DatabaseConnector, PluginDatabaseDependencies } from './types'; import { mysqlConnector, pgConnector, sqlite3Connector } from './connectors'; @@ -55,11 +55,12 @@ const ConnectorMapping: Record = { export function createDatabaseClient( dbConfig: Config, overrides?: Partial, + deps?: PluginDatabaseDependencies, ) { const client: DatabaseClient = dbConfig.getString('client'); return ( - ConnectorMapping[client]?.createClient(dbConfig, overrides) ?? + ConnectorMapping[client]?.createClient(dbConfig, overrides, deps) ?? knexFactory(mergeDatabaseConfig(dbConfig.get(), overrides)) ); } diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index 6426005f0e..299e18a111 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -18,8 +18,9 @@ import { Config } from '@backstage/config'; import { ensureDirSync } from 'fs-extra'; import knexFactory, { Knex } from 'knex'; import path from 'path'; +import { DevDataStore } from '@backstage/backend-dev-utils'; import { mergeDatabaseConfig } from '../config'; -import { DatabaseConnector } from '../types'; +import { DatabaseConnector, PluginDatabaseDependencies } from '../types'; /** * Creates a knex SQLite3 database connection @@ -30,22 +31,56 @@ import { DatabaseConnector } from '../types'; export function createSqliteDatabaseClient( dbConfig: Config, overrides?: Knex.Config, + deps?: PluginDatabaseDependencies, ) { const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); + const connConfig = knexConfig.connection as Knex.Sqlite3ConnectionConfig; // If storage on disk is used, ensure that the directory exists - if ( - (knexConfig.connection as Knex.Sqlite3ConnectionConfig).filename && - (knexConfig.connection as Knex.Sqlite3ConnectionConfig).filename !== - ':memory:' - ) { - const { filename } = knexConfig.connection as Knex.Sqlite3ConnectionConfig; - const directory = path.dirname(filename); - + if (connConfig.filename && connConfig.filename !== ':memory:') { + const directory = path.dirname(connConfig.filename); ensureDirSync(directory); } - const database = knexFactory(knexConfig); + let database: Knex; + + if (deps && connConfig.filename === ':memory:') { + // The dev store is used during watch mode to store and restore the database + // across reloads. It is only available when running the backend through + // `backstage-cli package start`. + const devStore = DevDataStore.get(); + const dataKey = `sqlite3-db-${deps.pluginMetadata.getId()}`; + + if (devStore) { + database = knexFactory({ + ...knexConfig, + connection: async () => { + // If seed data is available, use it to restore the database + const { data: seedData } = await devStore?.load(dataKey); + + return { + ...(knexConfig.connection as Knex.Sqlite3ConnectionConfig), + filename: seedData ?? ':memory:', + }; + }, + }); + } else { + database = knexFactory(knexConfig); + } + + if (devStore) { + // If the dev store is available we save the database state on shutdown + deps?.lifecycle?.addShutdownHook({ + async fn() { + const connection = await database.client.acquireConnection(); + const data = connection.serialize(); + await devStore.save(dataKey, data); + }, + }); + } + } else { + database = knexFactory(knexConfig); + } database.client.pool.on('createSuccess', (_eventId: any, resource: any) => { resource.run('PRAGMA foreign_keys = ON', () => {}); diff --git a/packages/backend-common/src/database/index.ts b/packages/backend-common/src/database/index.ts index 0dde1ec239..483dbd9e40 100644 --- a/packages/backend-common/src/database/index.ts +++ b/packages/backend-common/src/database/index.ts @@ -22,5 +22,8 @@ export * from './DatabaseManager'; */ export { createDatabaseClient, ensureDatabaseExists } from './connection'; -export type { PluginDatabaseManager } from './types'; +export type { + PluginDatabaseManager, + PluginDatabaseDependencies, +} from './types'; export { isDatabaseConflictError } from './util'; diff --git a/packages/backend-common/src/database/types.ts b/packages/backend-common/src/database/types.ts index 2d59cf636e..7e96c44071 100644 --- a/packages/backend-common/src/database/types.ts +++ b/packages/backend-common/src/database/types.ts @@ -14,11 +14,25 @@ * limitations under the License. */ +import { + LifecycleService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { Knex } from 'knex'; export type { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; +/** + * Service dependencies for `PluginDatabaseManager`. + * + * @public + */ +export type PluginDatabaseDependencies = { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; +}; + /** * DatabaseConnector manages an underlying Knex database driver. */ @@ -26,7 +40,11 @@ export interface DatabaseConnector { /** * createClient provides an instance of a knex database connector. */ - createClient(dbConfig: Config, overrides?: Partial): Knex; + createClient( + dbConfig: Config, + overrides?: Partial, + deps?: PluginDatabaseDependencies, + ): Knex; /** * createNameOverride provides a partial knex config sufficient to override a * database name. diff --git a/yarn.lock b/yarn.lock index 7143443674..650cb4c95d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3423,6 +3423,7 @@ __metadata: resolution: "@backstage/backend-common@workspace:packages/backend-common" dependencies: "@backstage/backend-app-api": "workspace:^" + "@backstage/backend-dev-utils": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" @@ -3519,7 +3520,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/backend-dev-utils@workspace:packages/backend-dev-utils": +"@backstage/backend-dev-utils@workspace:^, @backstage/backend-dev-utils@workspace:packages/backend-dev-utils": version: 0.0.0-use.local resolution: "@backstage/backend-dev-utils@workspace:packages/backend-dev-utils" dependencies: From cd4d0365ac9feddd40abe820d3c9097f92575202 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 1 Feb 2023 11:08:32 +0100 Subject: [PATCH 091/130] backend-next: use experimental start script Signed-off-by: Patrik Oldsberg --- packages/backend-next/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index cc537ef514..fb821e19eb 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -18,7 +18,7 @@ "backstage" ], "scripts": { - "start": "backstage-cli package start", + "start": "EXPERIMENTAL_BACKEND_START=1 backstage-cli package start", "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", From 03570c71e44b81f0da89d7cf244c79e4687e6046 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Feb 2023 16:07:08 +0100 Subject: [PATCH 092/130] backend-common: inline plugin database deps type Signed-off-by: Patrik Oldsberg --- packages/backend-common/api-report.md | 16 ++++++++-------- .../src/database/DatabaseManager.ts | 18 ++++++++++++++---- .../backend-common/src/database/connection.ts | 11 +++++++++-- .../src/database/connectors/sqlite3.ts | 11 +++++++++-- packages/backend-common/src/database/index.ts | 5 +---- packages/backend-common/src/database/types.ts | 15 ++++----------- 6 files changed, 45 insertions(+), 31 deletions(-) diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 8b6311f8b0..e77a58b34c 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -234,7 +234,10 @@ export class Contexts { export function createDatabaseClient( dbConfig: Config, overrides?: Partial, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ): Knex; // @public @@ -257,7 +260,10 @@ export function createStatusCheckRouter(options: { export class DatabaseManager { forPlugin( pluginId: string, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ): PluginDatabaseManager; static fromConfig( config: Config, @@ -577,12 +583,6 @@ export function notFoundHandler(): RequestHandler; export { PluginCacheManager }; -// @public -export type PluginDatabaseDependencies = { - lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; -}; - export { PluginDatabaseManager }; export { PluginEndpointDiscovery }; diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index d8205faec1..08979a92f8 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -27,9 +27,13 @@ import { ensureSchemaExists, normalizeConnection, } from './connection'; -import { PluginDatabaseDependencies, PluginDatabaseManager } from './types'; +import { PluginDatabaseManager } from './types'; import path from 'path'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + LifecycleService, + LoggerService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; import { stringifyError } from '@backstage/errors'; /** @@ -96,7 +100,10 @@ export class DatabaseManager { */ forPlugin( pluginId: string, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ): PluginDatabaseManager { const _this = this; @@ -312,7 +319,10 @@ export class DatabaseManager { */ private async getDatabase( pluginId: string, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ): Promise { if (this.databaseCache.has(pluginId)) { return this.databaseCache.get(pluginId)!; diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index 054ab7cfa8..ff10d349dd 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -19,9 +19,13 @@ import { JsonObject } from '@backstage/types'; import { InputError } from '@backstage/errors'; import knexFactory, { Knex } from 'knex'; import { mergeDatabaseConfig } from './config'; -import { DatabaseConnector, PluginDatabaseDependencies } from './types'; +import { DatabaseConnector } from './types'; import { mysqlConnector, pgConnector, sqlite3Connector } from './connectors'; +import { + LifecycleService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; type DatabaseClient = | 'pg' @@ -55,7 +59,10 @@ const ConnectorMapping: Record = { export function createDatabaseClient( dbConfig: Config, overrides?: Partial, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ) { const client: DatabaseClient = dbConfig.getString('client'); diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index 299e18a111..5a2dc46f18 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -20,7 +20,11 @@ import knexFactory, { Knex } from 'knex'; import path from 'path'; import { DevDataStore } from '@backstage/backend-dev-utils'; import { mergeDatabaseConfig } from '../config'; -import { DatabaseConnector, PluginDatabaseDependencies } from '../types'; +import { DatabaseConnector } from '../types'; +import { + LifecycleService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; /** * Creates a knex SQLite3 database connection @@ -31,7 +35,10 @@ import { DatabaseConnector, PluginDatabaseDependencies } from '../types'; export function createSqliteDatabaseClient( dbConfig: Config, overrides?: Knex.Config, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ) { const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); const connConfig = knexConfig.connection as Knex.Sqlite3ConnectionConfig; diff --git a/packages/backend-common/src/database/index.ts b/packages/backend-common/src/database/index.ts index 483dbd9e40..0dde1ec239 100644 --- a/packages/backend-common/src/database/index.ts +++ b/packages/backend-common/src/database/index.ts @@ -22,8 +22,5 @@ export * from './DatabaseManager'; */ export { createDatabaseClient, ensureDatabaseExists } from './connection'; -export type { - PluginDatabaseManager, - PluginDatabaseDependencies, -} from './types'; +export type { PluginDatabaseManager } from './types'; export { isDatabaseConflictError } from './util'; diff --git a/packages/backend-common/src/database/types.ts b/packages/backend-common/src/database/types.ts index 7e96c44071..621bcc676b 100644 --- a/packages/backend-common/src/database/types.ts +++ b/packages/backend-common/src/database/types.ts @@ -23,16 +23,6 @@ import { Knex } from 'knex'; export type { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; -/** - * Service dependencies for `PluginDatabaseManager`. - * - * @public - */ -export type PluginDatabaseDependencies = { - lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; -}; - /** * DatabaseConnector manages an underlying Knex database driver. */ @@ -43,7 +33,10 @@ export interface DatabaseConnector { createClient( dbConfig: Config, overrides?: Partial, - deps?: PluginDatabaseDependencies, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, ): Knex; /** * createNameOverride provides a partial knex config sufficient to override a From cbeba4b6f38d7db41f5b9d1f90585af2debaf614 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Feb 2023 16:07:49 +0100 Subject: [PATCH 093/130] backend-common: simplify SQLite setup + work around warning Signed-off-by: Patrik Oldsberg --- .../src/database/connectors/sqlite3.ts | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index 5a2dc46f18..e1ed6b18e9 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -43,47 +43,53 @@ export function createSqliteDatabaseClient( const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); const connConfig = knexConfig.connection as Knex.Sqlite3ConnectionConfig; + const filename = connConfig.filename ?? ':memory:'; + // If storage on disk is used, ensure that the directory exists - if (connConfig.filename && connConfig.filename !== ':memory:') { - const directory = path.dirname(connConfig.filename); + if (filename !== ':memory:') { + const directory = path.dirname(filename); ensureDirSync(directory); } let database: Knex; - if (deps && connConfig.filename === ':memory:') { + if (deps && filename === ':memory:') { // The dev store is used during watch mode to store and restore the database // across reloads. It is only available when running the backend through // `backstage-cli package start`. const devStore = DevDataStore.get(); - const dataKey = `sqlite3-db-${deps.pluginMetadata.getId()}`; if (devStore) { + const dataKey = `sqlite3-db-${deps.pluginMetadata.getId()}`; + + const connectionLoader = async () => { + // If seed data is available, use it tconnectionLoader restore the database + const { data: seedData } = await devStore.load(dataKey); + + return { + ...(knexConfig.connection as Knex.Sqlite3ConnectionConfig), + filename: seedData ?? ':memory:', + }; + }; + database = knexFactory({ ...knexConfig, - connection: async () => { - // If seed data is available, use it to restore the database - const { data: seedData } = await devStore?.load(dataKey); - - return { - ...(knexConfig.connection as Knex.Sqlite3ConnectionConfig), - filename: seedData ?? ':memory:', - }; - }, + connection: Object.assign(connectionLoader, { + // This is a workaround for the knex SQLite driver always warning when using a config loader + filename: ':memory:', + }), }); - } else { - database = knexFactory(knexConfig); - } - if (devStore) { // If the dev store is available we save the database state on shutdown - deps?.lifecycle?.addShutdownHook({ + deps.lifecycle.addShutdownHook({ async fn() { const connection = await database.client.acquireConnection(); const data = connection.serialize(); await devStore.save(dataKey, data); }, }); + } else { + database = knexFactory(knexConfig); } } else { database = knexFactory(knexConfig); From 4f5bf2835c43699d61d6313abde4ec9a2329d025 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Feb 2023 16:11:23 +0100 Subject: [PATCH 094/130] backend-app-api: tweak backend initializer to wait for startup before shutdown Signed-off-by: Patrik Oldsberg --- .../src/wiring/BackendInitializer.ts | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index d1139615f1..4bdee8b085 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -28,7 +28,7 @@ import { } from './types'; export class BackendInitializer { - #started = false; + #startPromise?: Promise; #features = new Map(); #registerInits = new Array(); #extensionPoints = new Map, unknown>(); @@ -75,30 +75,40 @@ export class BackendInitializer { } add(feature: BackendFeature, options?: TOptions) { - if (this.#started) { + if (this.#startPromise) { throw new Error('feature can not be added after the backend has started'); } this.#features.set(feature, options); } async start(): Promise { - if (this.#started) { + if (this.#startPromise) { throw new Error('Backend has already started'); } - this.#started = true; - for (const event of ['SIGTERM', 'SIGINT', 'beforeExit']) { - process.on(event, async () => { - try { - await this.stop(); - process.exit(0); - } catch (error) { - console.error(error); - process.exit(1); - } - }); - } + const exitHandler = async () => { + process.removeListener('SIGTERM', exitHandler); + process.removeListener('SIGINT', exitHandler); + process.removeListener('beforeExit', exitHandler); + try { + await this.stop(); + process.exit(0); + } catch (error) { + console.error(error); + process.exit(1); + } + }; + + process.addListener('SIGTERM', exitHandler); + process.addListener('SIGINT', exitHandler); + process.addListener('beforeExit', exitHandler); + + this.#startPromise = this.#doStart(); + await this.#startPromise; + } + + async #doStart(): Promise { // Initialize all root scoped services for (const ref of this.#serviceHolder.getServiceRefs()) { if (ref.scope === 'root') { @@ -189,9 +199,10 @@ export class BackendInitializer { } async stop(): Promise { - if (!this.#started) { + if (!this.#startPromise) { return; } + await this.#startPromise; const lifecycleService = await this.#serviceHolder.get( coreServices.rootLifecycle, From 81c06d5a4ad0e4579e40526dcb73e5f7728b21a3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Feb 2023 16:11:47 +0100 Subject: [PATCH 095/130] backend-dev-utils: remove message listener when IPC client reuqest times out Signed-off-by: Patrik Oldsberg --- packages/backend-dev-utils/src/ipcClient.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/backend-dev-utils/src/ipcClient.ts b/packages/backend-dev-utils/src/ipcClient.ts index d3fa184a3f..87c1596a39 100644 --- a/packages/backend-dev-utils/src/ipcClient.ts +++ b/packages/backend-dev-utils/src/ipcClient.ts @@ -84,10 +84,7 @@ export class BackstageIpcClient { return; } - const timeout = setTimeout(() => { - reject(new Error('IPC request timed out')); - }, 1000); - timeout.unref(); + let timeout: NodeJS.Timeout | undefined = undefined; const messageHandler = (response: Response) => { if (response?.type !== responseType) { @@ -111,6 +108,12 @@ export class BackstageIpcClient { process.removeListener('message', messageHandler); }; + timeout = setTimeout(() => { + reject(new Error(`IPC request '${method}' with ID ${id} timed out`)); + process.removeListener('message', messageHandler); + }, 5000); + timeout.unref(); + process.addListener('message', messageHandler as () => void); }); }); From 1e6f5fcfede627b980d8e627b94ed8b857219213 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Feb 2023 16:12:53 +0100 Subject: [PATCH 096/130] cli: rename ChannelServer to IpcServer Signed-off-by: Patrik Oldsberg --- .../src/lib/experimental/{ChannelServer.ts => IpcServer.ts} | 2 +- packages/cli/src/lib/experimental/ServerDataStore.ts | 4 ++-- packages/cli/src/lib/experimental/startBackendExperimental.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename packages/cli/src/lib/experimental/{ChannelServer.ts => IpcServer.ts} (98%) diff --git a/packages/cli/src/lib/experimental/ChannelServer.ts b/packages/cli/src/lib/experimental/IpcServer.ts similarity index 98% rename from packages/cli/src/lib/experimental/ChannelServer.ts rename to packages/cli/src/lib/experimental/IpcServer.ts index db949f95b8..8ceb72b046 100644 --- a/packages/cli/src/lib/experimental/ChannelServer.ts +++ b/packages/cli/src/lib/experimental/IpcServer.ts @@ -36,7 +36,7 @@ interface Request { const requestType = '@backstage/cli/channel/request'; const responseType = '@backstage/cli/channel/response'; -export class ChannelServer { +export class IpcServer { #generation = 1; #methods = new Map>(); diff --git a/packages/cli/src/lib/experimental/ServerDataStore.ts b/packages/cli/src/lib/experimental/ServerDataStore.ts index 29ac331de5..c0232157cb 100644 --- a/packages/cli/src/lib/experimental/ServerDataStore.ts +++ b/packages/cli/src/lib/experimental/ServerDataStore.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ChannelServer } from './ChannelServer'; +import { IpcServer } from './IpcServer'; interface StorageItem { generation: number; @@ -40,7 +40,7 @@ interface LoadResponse { } export class ServerDataStore { - static bind(server: ChannelServer): void { + static bind(server: IpcServer): void { const store = new Map(); server.registerMethod( diff --git a/packages/cli/src/lib/experimental/startBackendExperimental.ts b/packages/cli/src/lib/experimental/startBackendExperimental.ts index 94bf066925..c08fbda0da 100644 --- a/packages/cli/src/lib/experimental/startBackendExperimental.ts +++ b/packages/cli/src/lib/experimental/startBackendExperimental.ts @@ -19,7 +19,7 @@ import type { ChildProcess } from 'child_process'; import { fileURLToPath } from 'url'; import { isAbsolute as isAbsolutePath } from 'path'; import { FSWatcher, watch } from 'chokidar'; -import { ChannelServer } from './ChannelServer'; +import { IpcServer } from './IpcServer'; import { ServerDataStore } from './ServerDataStore'; import debounce from 'lodash/debounce'; import spawn from 'cross-spawn'; @@ -34,7 +34,7 @@ export async function startBackendExperimental(options: BackendServeOptions) { } // Set up the parent IPC server and bind the available services - const server = new ChannelServer(); + const server = new IpcServer(); ServerDataStore.bind(server); let exiting = false; From 04f8ef30a6804d9e8048b8d7e591c3b644d40889 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Mon, 6 Feb 2023 12:01:47 +0100 Subject: [PATCH 097/130] Update slow-moles-sin.md Signed-off-by: Ben Lambert --- .changeset/slow-moles-sin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/slow-moles-sin.md b/.changeset/slow-moles-sin.md index 13527b509e..140d9e7435 100644 --- a/.changeset/slow-moles-sin.md +++ b/.changeset/slow-moles-sin.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-circleci': minor +'@backstage/plugin-circleci': patch --- -Making workflow a link +Making workflow a link From acf6c8c10b2c9f5206456f3216280019a00e3df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 13:49:48 +0100 Subject: [PATCH 098/130] [TechRadar] Deprecate RadarEntry.url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/hip-chairs-double.md | 5 +++++ .changeset/slow-moles-sin.md | 2 +- plugins/tech-radar/src/api.ts | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/hip-chairs-double.md diff --git a/.changeset/hip-chairs-double.md b/.changeset/hip-chairs-double.md new file mode 100644 index 0000000000..73e939ba21 --- /dev/null +++ b/.changeset/hip-chairs-double.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-radar': minor +--- + +Deprecate `RadarEntry.url` - use `RadarEntry.links` instead diff --git a/.changeset/slow-moles-sin.md b/.changeset/slow-moles-sin.md index 140d9e7435..02e1952312 100644 --- a/.changeset/slow-moles-sin.md +++ b/.changeset/slow-moles-sin.md @@ -2,4 +2,4 @@ '@backstage/plugin-circleci': patch --- -Making workflow a link +Making workflow a link diff --git a/plugins/tech-radar/src/api.ts b/plugins/tech-radar/src/api.ts index 0fbc88255a..bf90cf0750 100644 --- a/plugins/tech-radar/src/api.ts +++ b/plugins/tech-radar/src/api.ts @@ -133,6 +133,8 @@ export interface RadarEntry { * @remarks * * You can use `#` if you don't want to provide any other url + * + * @deprecated Use {@link RadarEntry.links} instead */ url: string; /** From 3c5c6807fb81ad7aec78f097d92e71834ce88a19 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 6 Feb 2023 07:55:44 -0500 Subject: [PATCH 099/130] Remove title so it doesn't show up twice Signed-off-by: Taras Mankovski --- microsite/blog/2023-01-31-incremental-entity-provider.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsite/blog/2023-01-31-incremental-entity-provider.md b/microsite/blog/2023-01-31-incremental-entity-provider.md index ff5f346ccd..c14c3fc15d 100644 --- a/microsite/blog/2023-01-31-incremental-entity-provider.md +++ b/microsite/blog/2023-01-31-incremental-entity-provider.md @@ -4,8 +4,6 @@ author: Paul Cowan & Taras Mankovski authorURL: https://frontside.com/ --- -# Scaling Backstage Ingestion with Incremental Entity Providers - At the heart of [Backstage](https://backstage.io/) is the [Backstage Software Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), which is a data store that allows an organization to centralize and visualize its many software services and components. Backstage inspects and transforms an organization's disparate software services and parts into a centralized data store. ![catalog pipeline](assets/2023-01-31/catalog-pipeline.png) From 9a58b4127e6a4a5fad3000a84aa71135fa40ca13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 14:17:47 +0100 Subject: [PATCH 100/130] Build API report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/tech-radar/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/tech-radar/api-report.md b/plugins/tech-radar/api-report.md index e2744872d8..2836e04461 100644 --- a/plugins/tech-radar/api-report.md +++ b/plugins/tech-radar/api-report.md @@ -25,6 +25,7 @@ export interface RadarEntry { quadrant: string; timeline: Array; title: string; + // @deprecated url: string; } From 28f9883440f254856b08d35812772295262f527a Mon Sep 17 00:00:00 2001 From: LauraMoraB Date: Mon, 6 Feb 2023 14:55:39 +0100 Subject: [PATCH 101/130] fix(catalog/msgraph): read user.select from config Signed-off-by: LauraMoraB --- .changeset/silly-buckets-joke.md | 5 +++++ .../src/microsoftGraph/config.test.ts | 4 ++++ .../src/microsoftGraph/config.ts | 2 ++ 3 files changed, 11 insertions(+) create mode 100644 .changeset/silly-buckets-joke.md diff --git a/.changeset/silly-buckets-joke.md b/.changeset/silly-buckets-joke.md new file mode 100644 index 0000000000..bd77f2ed05 --- /dev/null +++ b/.changeset/silly-buckets-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Fixed bug in readProviderConfig as it wasn't reading the user.select field expected from the app-config.yaml configuration diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts index 68c174665d..b02894dfd9 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts @@ -52,6 +52,7 @@ describe('readMicrosoftGraphConfig', () => { authority: 'https://login.example.com/', userExpand: 'manager', userFilter: 'accountEnabled eq true', + userSelect: ['id', 'displayName', 'department'], groupExpand: 'member', groupSelect: ['id', 'displayName', 'description'], groupFilter: 'securityEnabled eq false', @@ -69,6 +70,7 @@ describe('readMicrosoftGraphConfig', () => { authority: 'https://login.example.com/', userExpand: 'manager', userFilter: 'accountEnabled eq true', + userSelect: ['id', 'displayName', 'department'], groupExpand: 'member', groupSelect: ['id', 'displayName', 'description'], groupFilter: 'securityEnabled eq false', @@ -167,6 +169,7 @@ describe('readProviderConfigs', () => { user: { expand: 'manager', filter: 'accountEnabled eq true', + select: ['id', 'displayName', 'department'], }, group: { expand: 'member', @@ -196,6 +199,7 @@ describe('readProviderConfigs', () => { queryMode: 'advanced', userExpand: 'manager', userFilter: 'accountEnabled eq true', + userSelect: ['id', 'displayName', 'department'], groupExpand: 'member', groupSelect: ['id', 'displayName', 'description'], groupFilter: 'securityEnabled eq false', diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts index 506951ff10..cf4ec646a2 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts @@ -264,6 +264,7 @@ export function readProviderConfig( const userExpand = config.getOptionalString('user.expand'); const userFilter = config.getOptionalString('user.filter'); + const userSelect = config.getOptionalStringArray('user.select'); const groupExpand = config.getOptionalString('group.expand'); const groupFilter = config.getOptionalString('group.filter'); @@ -318,6 +319,7 @@ export function readProviderConfig( tenantId, userExpand, userFilter, + userSelect, groupExpand, groupFilter, groupSearch, From 4382e0c7a2ce78db27d668d936f5c3aae41d286d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 15:24:33 +0100 Subject: [PATCH 102/130] Remove deprecated url from `sample.ts` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/tech-radar/src/sample.ts | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/plugins/tech-radar/src/sample.ts b/plugins/tech-radar/src/sample.ts index 1c4f7a1a01..0dc647ac54 100644 --- a/plugins/tech-radar/src/sample.ts +++ b/plugins/tech-radar/src/sample.ts @@ -45,12 +45,16 @@ entries.push({ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua', }, ], - url: 'https://www.javascript.com/', + url: '#', key: 'javascript', id: 'javascript', title: 'JavaScript', quadrant: 'languages', links: [ + { + url: 'https://www.javascript.com/', + title: 'Learn more', + }, { url: 'https://www.typescriptlang.org/', title: 'TypeScript', @@ -87,7 +91,13 @@ entries.push({ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur', }, ], - url: 'https://webpack.js.org/', + url: '#', + links: [ + { + url: 'https://webpack.js.org/', + title: 'Learn more', + }, + ], key: 'webpack', id: 'webpack', title: 'Webpack', @@ -101,7 +111,13 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: 'https://reactjs.org/', + url: '#', + links: [ + { + url: 'https://reactjs.org/', + title: 'Learn more', + }, + ], key: 'react', id: 'react', title: 'React', @@ -170,7 +186,13 @@ entries.push({ description: 'long description', }, ], - url: 'https://github.com', + url: '#', + links: [ + { + url: 'https://reactjs.org/', + title: 'Learn more', + }, + ], key: 'github-actions', id: 'github-actions', title: 'GitHub Actions', From 8179f9df3eb0264537f798b4c875f1534a3093e5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 14:25:09 +0000 Subject: [PATCH 103/130] chore(deps): update dependency @types/node to v16.18.12 Signed-off-by: Renovate Bot --- yarn.lock | 164 +++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/yarn.lock b/yarn.lock index 15cc1e24a9..259b321cd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,7 +3362,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -3702,7 +3702,7 @@ __metadata: "@types/jest": ^29.0.0 "@types/minimatch": ^5.0.0 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/npm-packlist": ^3.0.0 "@types/recursive-readdir": ^2.2.0 "@types/rollup-plugin-peer-deps-external": ^2.2.0 @@ -3804,7 +3804,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@types/jscodeshift": ^0.11.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 chalk: ^4.0.0 commander: ^9.1.0 jscodeshift: ^0.14.0 @@ -3827,7 +3827,7 @@ __metadata: "@types/json-schema": ^7.0.6 "@types/json-schema-merge-allof": ^0.6.0 "@types/mock-fs": ^4.10.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/yup": ^0.29.13 ajv: ^8.10.0 chokidar: ^3.5.2 @@ -3870,7 +3870,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -3968,7 +3968,7 @@ __metadata: "@types/d3-zoom": ^3.0.1 "@types/dagre": ^0.7.44 "@types/google-protobuf": ^3.7.2 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 @@ -4025,7 +4025,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -4049,7 +4049,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/inquirer": ^8.1.3 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/recursive-readdir": ^2.2.0 chalk: ^4.0.0 commander: ^9.1.0 @@ -4174,7 +4174,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4266,7 +4266,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/git-url-parse": ^9.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-markdown: ^8.0.0 @@ -4318,7 +4318,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4347,7 +4347,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4375,7 +4375,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-ga: ^3.3.0 @@ -4401,7 +4401,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.10.1 @@ -4445,7 +4445,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/swagger-ui-react": ^4.1.1 cross-fetch: ^3.1.5 graphiql: ^1.8.8 @@ -4480,7 +4480,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4656,7 +4656,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 luxon: ^3.0.0 @@ -4718,7 +4718,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -4770,7 +4770,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4862,7 +4862,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -5560,7 +5560,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.25.1 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 circleci-api: ^4.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 @@ -5593,7 +5593,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -5625,7 +5625,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.27.1 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.1 luxon: ^3.0.0 @@ -5687,7 +5687,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/highlightjs": ^10.1.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 highlight.js: ^10.6.0 @@ -5720,7 +5720,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -5751,7 +5751,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 jsonschema: ^1.2.6 msw: ^0.49.0 @@ -5792,7 +5792,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/pluralize": ^0.0.29 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 @@ -5834,7 +5834,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 express: ^4.18.1 msw: ^0.49.0 react-use: ^17.2.4 @@ -6017,7 +6017,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -6048,7 +6048,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 classnames: ^2.2.6 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6078,7 +6078,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6108,7 +6108,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6140,7 +6140,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.3.3 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/sanitize-html": ^2.6.2 classnames: ^2.3.1 cross-fetch: ^3.1.5 @@ -6173,7 +6173,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 peerDependencies: @@ -6202,7 +6202,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6238,7 +6238,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6272,7 +6272,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6304,7 +6304,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6336,7 +6336,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6366,7 +6366,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6398,7 +6398,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/lodash": ^4.14.173 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -6428,7 +6428,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/codemirror": ^5.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 graphiql: ^1.5.12 graphql: ^16.0.0 @@ -6484,7 +6484,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 graphql-voyager: ^1.0.0-rc.31 msw: ^0.49.0 @@ -6538,7 +6538,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -6572,7 +6572,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.26.0 luxon: ^3.0.0 @@ -6640,7 +6640,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/testing-library__jest-dom": ^5.9.1 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6696,7 +6696,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 jest-when: ^3.1.0 msw: ^0.49.0 @@ -6784,7 +6784,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cronstrue: ^2.2.0 cross-fetch: ^3.1.5 @@ -6846,7 +6846,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6897,7 +6897,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6926,7 +6926,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6955,7 +6955,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 p-limit: ^3.1.0 @@ -6988,7 +6988,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7042,7 +7042,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -7203,7 +7203,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -7290,7 +7290,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -7342,7 +7342,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 command-exists: ^1.2.9 fs-extra: ^10.0.1 jest-when: ^3.1.0 @@ -7556,7 +7556,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@uiw/react-codemirror": ^4.9.3 classnames: ^2.2.6 cross-fetch: ^3.1.5 @@ -7761,7 +7761,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -7794,7 +7794,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7825,7 +7825,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/zen-observable": ^0.8.2 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -7893,7 +7893,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -7923,7 +7923,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7997,7 +7997,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -8109,7 +8109,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -8140,7 +8140,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/color": ^3.0.1 "@types/d3-force": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 color: ^4.0.1 cross-fetch: ^3.1.5 @@ -8175,7 +8175,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8243,7 +8243,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 git-url-parse: ^13.0.0 @@ -8356,7 +8356,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.2.2 "@types/event-source-polyfill": ^1.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 canvas: ^2.10.2 cross-fetch: ^3.1.5 dompurify: ^2.2.9 @@ -8418,7 +8418,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8471,7 +8471,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -8530,7 +8530,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8558,7 +8558,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -8633,7 +8633,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 zen-observable: ^0.10.0 @@ -10546,7 +10546,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -10569,7 +10569,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 msw: ^0.49.0 react-use: ^17.2.4 peerDependencies: @@ -13491,7 +13491,7 @@ __metadata: "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 "@types/http-proxy": ^1.17.4 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/serve-handler": ^6.1.0 "@types/webpack-env": ^1.15.3 commander: ^9.1.0 @@ -14841,7 +14841,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.0.0, @types/node@npm:^16.11.26, @types/node@npm:^16.9.2": +"@types/node@npm:^16.0.0, @types/node@npm:^16.9.2": version: 16.18.11 resolution: "@types/node@npm:16.18.11" checksum: 2a3b1da13063debe6e26f732defb5f03ef4ef732c3e08daba838d8850433bd00e537ce1a97ce9bcfc4b15db5218d701d1265fae94e0d6926906bec157e6b46e0 @@ -20874,7 +20874,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/errors": "workspace:^" "@types/fs-extra": ^9.0.1 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/puppeteer": ^5.4.4 chalk: ^4.0.0 commander: ^9.1.0 @@ -22194,7 +22194,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/jquery": ^3.3.34 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 @@ -34251,7 +34251,7 @@ __metadata: "@spotify/eslint-plugin": ^14.1.3 "@spotify/prettier-config": ^14.0.0 "@techdocs/cli": "workspace:*" - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/webpack": ^5.28.0 command-exists: ^1.2.9 concurrently: ^7.0.0 @@ -36275,7 +36275,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": ^16.0.0 "@types/react-dom": "*" cross-env: ^7.0.0 cypress: ^10.0.0 From 5b5d7d416ffca72ce84d0bf844a3938a9aacf777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 15:27:22 +0100 Subject: [PATCH 104/130] Update changeset with migration diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/hip-chairs-double.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.changeset/hip-chairs-double.md b/.changeset/hip-chairs-double.md index 73e939ba21..722795d80d 100644 --- a/.changeset/hip-chairs-double.md +++ b/.changeset/hip-chairs-double.md @@ -3,3 +3,18 @@ --- Deprecate `RadarEntry.url` - use `RadarEntry.links` instead + +```diff +- url: 'https://www.javascript.com/', ++ url: '#', + key: 'javascript', + id: 'javascript', + title: 'JavaScript', + quadrant: 'languages', + links: [ ++ { ++ url: 'https://www.javascript.com/', ++ title: 'Learn more', ++ }, + ], +``` From a6aff183ba79ebb4531d30a2a6d89b9ae3013ce1 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 15:28:43 +0100 Subject: [PATCH 105/130] chore: updating the breaking changes Signed-off-by: blam --- .github/workflows/automate_changeset_feedback.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 11fef75b9b..aaf0bb1294 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -31,10 +31,10 @@ jobs: - uses: backstage/actions/changeset-feedback@v0.6.0 name: Generate feedback with: - diffRef: 'origin/master' + diff-ref: 'origin/master' marker: issue-number: ${{ github.event.pull_request.number }} - botUsername: backstage-goalie[bot] + bot-username: backstage-goalie[bot] app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} installation-id: ${{ secrets.BACKSTAGE_GOALIE_INSTALLATION_ID }} From cd065df85f7814e3d586e868e01cf37f39f5cc77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 14:35:46 +0000 Subject: [PATCH 106/130] chore(deps): update dependency @types/node to v18.11.19 Signed-off-by: Renovate Bot --- yarn.lock | 160 +++++++++++++++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/yarn.lock b/yarn.lock index 15cc1e24a9..a3e4f754b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,7 +3362,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -3652,7 +3652,7 @@ __metadata: resolution: "@backstage/cli-common@workspace:packages/cli-common" dependencies: "@backstage/cli": "workspace:^" - "@types/node": ^16.0.0 + "@types/node": "*" languageName: unknown linkType: soft @@ -3702,7 +3702,7 @@ __metadata: "@types/jest": ^29.0.0 "@types/minimatch": ^5.0.0 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/npm-packlist": ^3.0.0 "@types/recursive-readdir": ^2.2.0 "@types/rollup-plugin-peer-deps-external": ^2.2.0 @@ -3804,7 +3804,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@types/jscodeshift": ^0.11.0 - "@types/node": ^16.11.26 + "@types/node": "*" chalk: ^4.0.0 commander: ^9.1.0 jscodeshift: ^0.14.0 @@ -3827,7 +3827,7 @@ __metadata: "@types/json-schema": ^7.0.6 "@types/json-schema-merge-allof": ^0.6.0 "@types/mock-fs": ^4.10.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/yup": ^0.29.13 ajv: ^8.10.0 chokidar: ^3.5.2 @@ -3851,7 +3851,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" - "@types/node": ^16.0.0 + "@types/node": "*" lodash: ^4.17.21 languageName: unknown linkType: soft @@ -3870,7 +3870,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -3968,7 +3968,7 @@ __metadata: "@types/d3-zoom": ^3.0.1 "@types/dagre": ^0.7.44 "@types/google-protobuf": ^3.7.2 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 @@ -4025,7 +4025,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -4049,7 +4049,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/inquirer": ^8.1.3 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/recursive-readdir": ^2.2.0 chalk: ^4.0.0 commander: ^9.1.0 @@ -4085,7 +4085,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" react-use: ^17.2.4 zen-observable: ^0.10.0 peerDependencies: @@ -4174,7 +4174,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4318,7 +4318,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4347,7 +4347,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4375,7 +4375,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-ga: ^3.3.0 @@ -4401,7 +4401,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.10.1 @@ -4445,7 +4445,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/swagger-ui-react": ^4.1.1 cross-fetch: ^3.1.5 graphiql: ^1.8.8 @@ -4656,7 +4656,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 luxon: ^3.0.0 @@ -4718,7 +4718,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -4770,7 +4770,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4862,7 +4862,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -5560,7 +5560,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.25.1 - "@types/node": ^16.11.26 + "@types/node": "*" circleci-api: ^4.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 @@ -5593,7 +5593,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -5625,7 +5625,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.27.1 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 humanize-duration: ^3.27.1 luxon: ^3.0.0 @@ -5687,7 +5687,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/highlightjs": ^10.1.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 highlight.js: ^10.6.0 @@ -5751,7 +5751,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 jsonschema: ^1.2.6 msw: ^0.49.0 @@ -5792,7 +5792,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/pluralize": ^0.0.29 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 @@ -6017,7 +6017,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -6048,7 +6048,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" classnames: ^2.2.6 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6078,7 +6078,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6108,7 +6108,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6140,7 +6140,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.3.3 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/sanitize-html": ^2.6.2 classnames: ^2.3.1 cross-fetch: ^3.1.5 @@ -6173,7 +6173,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 peerDependencies: @@ -6202,7 +6202,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6238,7 +6238,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6272,7 +6272,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6336,7 +6336,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6366,7 +6366,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6398,7 +6398,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/lodash": ^4.14.173 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -6428,7 +6428,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/codemirror": ^5.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 graphiql: ^1.5.12 graphql: ^16.0.0 @@ -6538,7 +6538,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -6572,7 +6572,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 humanize-duration: ^3.26.0 luxon: ^3.0.0 @@ -6640,7 +6640,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/testing-library__jest-dom": ^5.9.1 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6696,7 +6696,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 jest-when: ^3.1.0 msw: ^0.49.0 @@ -6784,7 +6784,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cronstrue: ^2.2.0 cross-fetch: ^3.1.5 @@ -6846,7 +6846,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6897,7 +6897,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6955,7 +6955,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 p-limit: ^3.1.0 @@ -6988,7 +6988,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7042,7 +7042,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -7290,7 +7290,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -7342,7 +7342,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.11.26 + "@types/node": "*" command-exists: ^1.2.9 fs-extra: ^10.0.1 jest-when: ^3.1.0 @@ -7556,7 +7556,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 - "@types/node": ^16.11.26 + "@types/node": "*" "@uiw/react-codemirror": ^4.9.3 classnames: ^2.2.6 cross-fetch: ^3.1.5 @@ -7761,7 +7761,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -7794,7 +7794,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7825,7 +7825,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/zen-observable": ^0.8.2 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -7893,7 +7893,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -7923,7 +7923,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7997,7 +7997,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -8109,7 +8109,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -8140,7 +8140,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/color": ^3.0.1 "@types/d3-force": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 color: ^4.0.1 cross-fetch: ^3.1.5 @@ -8175,7 +8175,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8243,7 +8243,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 git-url-parse: ^13.0.0 @@ -8356,7 +8356,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.2.2 "@types/event-source-polyfill": ^1.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" canvas: ^2.10.2 cross-fetch: ^3.1.5 dompurify: ^2.2.9 @@ -8418,7 +8418,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8471,7 +8471,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -8558,7 +8558,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -8576,7 +8576,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -8633,7 +8633,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 zen-observable: ^0.10.0 @@ -10546,7 +10546,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": ^16.11.26 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -10569,7 +10569,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" msw: ^0.49.0 react-use: ^17.2.4 peerDependencies: @@ -13491,7 +13491,7 @@ __metadata: "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 "@types/http-proxy": ^1.17.4 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/serve-handler": ^6.1.0 "@types/webpack-env": ^1.15.3 commander: ^9.1.0 @@ -14800,9 +14800,9 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>= 8, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:^18.11.17": - version: 18.11.18 - resolution: "@types/node@npm:18.11.18" - checksum: 03f17f9480f8d775c8a72da5ea7e9383db5f6d85aa5fefde90dd953a1449bd5e4ffde376f139da4f3744b4c83942166d2a7603969a6f8ea826edfb16e6e3b49d + version: 18.11.19 + resolution: "@types/node@npm:18.11.19" + checksum: d7cd19fcfc59cbdd3f9ba0b4072cb7adc21bd575bd8eb7d7e698975e63564aaa83f03434f32b12331f84f73d0b369d9cbe2371e359d9d7f5c3361f4987f4f7da languageName: node linkType: hard @@ -14841,7 +14841,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.0.0, @types/node@npm:^16.11.26, @types/node@npm:^16.9.2": +"@types/node@npm:^16.9.2": version: 16.18.11 resolution: "@types/node@npm:16.18.11" checksum: 2a3b1da13063debe6e26f732defb5f03ef4ef732c3e08daba838d8850433bd00e537ce1a97ce9bcfc4b15db5218d701d1265fae94e0d6926906bec157e6b46e0 @@ -20874,7 +20874,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/errors": "workspace:^" "@types/fs-extra": ^9.0.1 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/puppeteer": ^5.4.4 chalk: ^4.0.0 commander: ^9.1.0 @@ -22194,7 +22194,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/jquery": ^3.3.34 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 @@ -34251,7 +34251,7 @@ __metadata: "@spotify/eslint-plugin": ^14.1.3 "@spotify/prettier-config": ^14.0.0 "@techdocs/cli": "workspace:*" - "@types/node": ^16.11.26 + "@types/node": "*" "@types/webpack": ^5.28.0 command-exists: ^1.2.9 concurrently: ^7.0.0 @@ -36275,7 +36275,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.11.26 + "@types/node": "*" "@types/react-dom": "*" cross-env: ^7.0.0 cypress: ^10.0.0 From 6c54bf08a5a040dce86758e51a193615a5d68b34 Mon Sep 17 00:00:00 2001 From: Lee Mills Date: Mon, 6 Feb 2023 10:49:28 +0000 Subject: [PATCH 107/130] Adding whats ahead blog post and image asset Signed-off-by: Lee Mills --- .../blog/2023-02-06-whats-ahead-in-2023.md | 58 ++++++++++++++++++ .../blog/assets/2023-02-06/whats-ahead.png | Bin 0 -> 150199 bytes 2 files changed, 58 insertions(+) create mode 100644 microsite/blog/2023-02-06-whats-ahead-in-2023.md create mode 100644 microsite/blog/assets/2023-02-06/whats-ahead.png diff --git a/microsite/blog/2023-02-06-whats-ahead-in-2023.md b/microsite/blog/2023-02-06-whats-ahead-in-2023.md new file mode 100644 index 0000000000..a9f06d9ea0 --- /dev/null +++ b/microsite/blog/2023-02-06-whats-ahead-in-2023.md @@ -0,0 +1,58 @@ +--- +title: What’s Ahead in 2023? +author: Lee Mills +authorURL: https://github.com/leemills83 +--- + +# What’s Ahead in 2023? + +![Fewer “vibes” and more climbs (up the contribution ladder)](assets/2023-02-06/whats-ahead.png) + +Okay, everyone. Time to move on from all the 2022 reflections, Top 10 lists, and Wrapped(s), and turn our gaze in the other direction — towards 2023. As the Backstage team moves into the new year, we find ourselves asking those perennial questions: How can we continue to grow and enhance the Backstage community? How can we improve the experience for those adopting Backstage? How can we give the best support to Backstage contributors? + +Well, we don’t have all the answers, but we do have some. So we wanted to share more on three ongoing initiatives aimed at moving the needle forward for these three distinct, yet interrelated, questions. + +- Grow your influence by climbing the contribution ladder +- Come join our SIGs (and push for new ones) +- Work with us to drive a better experience for Backstage adopters + +# Climb the contribution ladder + +Truth be told, we’ve been wanting to grow the maintainer team for some time, and have had multiple requests from contributors and contributing organizations wanting to become maintainers. But, so far, we just haven’t managed to get anything off the ground. What we see is that becoming a maintainer is too hard and the current ownership model doesn’t work. In short, we need to update Backstage governance. + +To address this, we are proposing (RFC links below) the following three initiatives: + +- Introduce more steps in between first-time contributor and maintainer in the form of a contributor ladder (loosely based on the [CNCF Project Template](https://github.com/cncf/project-template/blob/main/CONTRIBUTOR_LADDER.md)). The ladder might look as follows: Contributor, Org Member, Reviewer, Maintainer, Project Maintainer. +- Change the ownership model, for example, divide the project up into several project areas, each covering particular parts of the project. +- Introduce a new process for large contributions in the form of Backstage Enhancement Proposals (BEPs). + +Read more and join the already rich discussion in [RFC: Updated Backstage Governance](https://github.com/backstage/backstage/issues/15317) and [RFC: Backstage Enhancement Proposals](https://github.com/backstage/backstage/issues/15318). The RFC will be open until 13/02/2023. + +# Come join our SIGs (and push for new ones) + +As many of you know, SIG stands for Special Interest Group. The purpose of a SIG is to bring together maintainers and interested contributors within a certain specialist area to discuss, debate, spread knowledge, brainstorm - and drive the overall project forward. They’re also a lot of fun! + +We currently have two SIGs up and running: + +- [Catalog](https://github.com/backstage/community/tree/main/sigs/sig-catalog?utm_campaign=Newsletters%20Sent&utm_medium=email&_hsmi=241130993&_hsenc=p2ANqtz-9fx-nTmQga2j12NqzZaL-lYPq-xQjDoXcWKe_JUoa2RGlK65R9j0CIygfYILr0tGEQSvRVN9xzhiJ6LmVxBthKnStfSA&utm_content=241130993&utm_source=hs_email) started up last June and covers all aspects of the systems and components related to the Software Catalog. The group runs every second Tuesday between 16:00 and 17:00 CET. Just show up or hop into [#catalog](https://discord.com/channels/687207715902193673/923144214580191282) (Discord), if you want to talk to us first. Here are the [notes from our sessions so far](https://docs.google.com/document/d/1xg57CDfX2R5jTGbbWANzifm2szEb664kuTwfEEvKgZU/edit#heading=h.iw0hjv6xfb9e). +- [Adoption](https://github.com/backstage/community/tree/main/sigs/sig-adoption?utm_campaign=Newsletters%20Sent&utm_medium=email&_hsmi=241130993&_hsenc=p2ANqtz-9CAxtT8JSeJZPh41o3ZnDBi0VBFpbL6fEqXInr9HHOFhU3JAo8HekKg1JzE5rhGP5zOaHcsYDd1YxJWNHw-eSk5mLWkA&utm_content=241130993&utm_source=hs_email) kicked off in December and covers all aspects of the Backstage adoption journey including documentation and tutorials. In the first two meetups, we discussed adoption challenges based on user persona, collecting data about those challenges, and bootstrapping the Catalog to improve metadata quality for adopters. This one runs every second Thursday between 16:00 and 17:00 CET. Again just show up or, if you want to talk to us first, you can do so in [#adoption](https://discord.com/channels/687207715902193673/1045031039061479536) (Discord). Here are the [notes from our sessions](https://docs.google.com/document/d/1zeYSzXYgh897bxPQmwy2btOwCFlEjlUDLf00riySLRc/edit?_hsmi=241130993&_hsenc=p2ANqtz-_nPuE5hW4fnbhJ5-uK8r5HisSTU7rkslKirvqu0g-vdynbitPVYF2AVLAatos2dLoxb2Bgx_g4LBgH02kXoq7_Acd43Q#heading=h.iw0hjv6xfb9e) so far. + +So that’s SIGs. Get involved and join the ones we have up and running. And/or feel free to propose new ones. + +# Help drive a better experience for all adopters + +Throughout all stages of adopting Backstage in an organization — initial discovery, evaluation and POC, and scaling to the wider org — we have identified opportunities where we want to make the journey for Backstage adopters faster and smoother. + +We’ll go step-by-step, but initial plans are to: + +- Make it easier to install, customize, and maintain Backstage +- Provide Golden Paths for adoption +- Improve the experience and the content on [backstage.io](https://backstage.io/docs/overview/what-is-backstage) + +As we move forward, we’d love you to be part of it. Here are some ways that you can contribute to this initiative: + +- Simply give us feedback. [Tell us about your adoption journey](https://spotify.co1.qualtrics.com/jfe/form/SV_cuxhYPB6oQDu7gW). +- Contribute to the pages and documentation on [backstage.io](https://backstage.io/docs/overview/what-is-backstage) +- Pay it forward by making code contributions even during your adoption journey (if you are on one) +- Join the [Adoption](https://github.com/backstage/community/tree/main/sigs/sig-adoption?utm_campaign=Newsletters%20Sent&utm_medium=email&_hsmi=241130993&_hsenc=p2ANqtz-9CAxtT8JSeJZPh41o3ZnDBi0VBFpbL6fEqXInr9HHOFhU3JAo8HekKg1JzE5rhGP5zOaHcsYDd1YxJWNHw-eSk5mLWkA&utm_content=241130993&utm_source=hs_email) SIG +- Chat with us on [#adoption](https://discord.com/channels/687207715902193673/1045031039061479536) (Discord) diff --git a/microsite/blog/assets/2023-02-06/whats-ahead.png b/microsite/blog/assets/2023-02-06/whats-ahead.png new file mode 100644 index 0000000000000000000000000000000000000000..e49f68149d7cf72acf5d06b008b4cd3b74660d18 GIT binary patch literal 150199 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

Y=)q0dY> zT?F`?fpgw&xi^+o{Gu-a=Uw~5i{?23e2}oZ-p~SFv!4BfZj*^uD|LXUW3oM22Be9( z{qrmpkOosbRh2u1lm}|Q1El@Ryg~8o2=7>XZ-SdJQ@pe#snRU$Uc8XlYg3@c^gox@ zMtdH;cvJt^-yr}+U5*531yB4{|=2_WGZh0PXNpyUxShu31t7hvzmtrcMG$Jfy z=Nv6NP%u=3tS`4J6>3LEFK3`RC{ zOrz8S{6o`a`D=8!(Uzu7*mjqtFUfot*X*6y9}BD{ak0!qqf1LzvXw!q8E$P$UX$*d z)yO1SQ%Qt=`J>}e<*?Vzm()iPEaf0 zYBh`hWZ~w1g75la(cL|L0HM3-gl8X{24uz?;T6FD6}0mvt2`|eQYwOH05Z%5R{~?x zCpK_p{P$ESa{}=5E6%K-b@^`uao^^qs;nyaujmBEN|3JtrlrLOy*_8{sIwoSxcj1f zySRkKR1a$@PlqopC2&bpepVMw!HKhO?(Qz2VfEaZo`iwh1RSSIeZKf=s9Q0=-zs~BC&b@(HnjG!&e68n6p9CQh zX(omPPE53f4nh0>&xeG3!G;4c<3fM)=l5Z`JIab65Q58grCGLi-I9S$C43YPhpQLn zJ_FfZm|(02vfOmRB3m#Uv@~w2ecAh6DRg3&1LehZnriDd$f^QWo9no3$bm7$#e^Fl zHt&A&D*?n+;Cj1PDax$7I-!&Aw-z>!sCvX_u&Khsx0edtO=1=*?qST(otMek(iHC+Z}yI=M`mUXK9QhB zRyu~OKM0w#C)u@(W?y;p%0Fw>XX#upqALtiMQ>Ma@Ds??HA$XH{HFx~rAO*hzT9kj z2;~VzrI-IXz+QPN_yB|)07Sm>N^2oImjN3pRpQ zqZ*MTQPI(==yB0M4EV%h>6P|Fd8(GK}mRZXP>`CaK_gz7Iv0_3FE(46DP`-mbwMkACqZGEGx%g6n1J#A|Mrx-mLa z%_%JFJiqn#zxhKrYFNK)4g7MmPyTC1z|jHG1<0-#ZHvXubY?xxlr3d6GpyiRa0LHR zVuOA57Ed+M8^$3jSP0ZZFCI$Z?yxLyslX$BE3l>`E1jyHmeUb){zvLZ8&-Y zL3o_{;ja^fJpF4GWNazBz{F_ED!MXlO}3X8G*keW`(L4~NenAWx2{IGn`kPxcT;}N zeK)`cx|^538uXP{CiLmJw-+6KOzSM2wHGi)68dmcw4&2}y>myj! z9d2Rcxh6K%FJ{{=G2Lwjf^Qih3HEt&tRoVl6?u>yW8(iIb8lxh!1KE@jV9*YO#g#_ zX*V;oS~b6bkX1R6v)?uVDv0!HW4T%^{c9HGpymj1Ic}hUR|mLMPF!z2){&%LR`(9L&G8{Sk)jX5L09_;_a1U8F-riz_vb=y7O{~A6+m$4;T z`8ZLF*+QMA7Mh^w?Y6-I4V00qH~ZBei-0Hwj`&wF@FF!O(kf1(_|qVbs7y1`_AU=> zzOx*F0WiB;**HseQs;;H)#XZ|!`bxiNvg_B=srXHx>gp)IYz4netw^=@($$Qk0Ql(tQQ zjpS;Vn1io-Rl}3wY>Nip`I^K`t>3!FSr8Qn$@+bBK}&h2)pkXV|1lvrP;4{uLs+-k zQj5)vRzm=>lD4`Ycy3PnoTVpCmHk@Er6T;~c?8F^++YE2Ij4ZeX@7mB8|VxzjDM-wsgj_Qyrs3@kS8?uRIF+bPOys}5^@p*)8TbX|45c*$<$ zhr{*3;{vHwxHX>*MLpc}PC&BM_JiOiPikNFa z;2ocsIkd^GedY?P6UvqD1?{qHz-9}j9U%dSlE=|5z>-K#BU+Wi4>Mhk zcZ*Asowpb+d#`YQVdq3VWjIgm(Iottet5snxzuUkz~A($W>+L#gY9k=tGX$THWygu7??^{KZPjeV`4RE1KaM!>-eykO| zi@{pp!h6uZJ?$S4`vfu`UddAF-F3@S)<{1ax>gl`MvNY`A=2m{np+QCa*_*B&|hC= zp9R1qypvM)f(s2n*_bU@mM#^vp%i`C8J^|`g*O_JW8}2W5O>;OVB2C%~NvU+=uF;udz{0rH)SI=I<$cpbV7lt&jOlX7 z$IV#s^B6l$Ar$rh5B>Cx9&7xaB1Ks*lS(~cu~&Ojr#@T2aTa9HIW@9>eh0!TFT?ucY8bxfQ{L?+G)tbH1 zAC)`)RkcQNHUQQ)0Yrj16mp;_x(gO{#V&XgQTd{vMvkBJ|8(?>W-lgBd{Va_`wsA6 zf3o9DZ#2LxMH@Z02wX&bPlMZJ=yYjOg(J%qNbOa%KiW&_d04~!B6!(;fP+83A4rp3 z^?`L0AiEN$v-%_7Ufn+i&gHJj{Y$XDM8LTxu7p#%u!2^dTgd4X335Q0<_y_p%ufIZ z@%Lz?-bEH*VVA}yfVjv0w?TFJYO;*9uO?4s+5-G{;Yw@1TL-F6syc}VZWaT!s@q-o z1HIAmTr)45b2?QYi95~-|Kz3_(RT_ z*?X_GK6&5wTKkU5Zi6)Uy-4_?@qXbjXf?oO9kx%`c7PaKEgs~(l zQ(cfj+;B^43(&d`Y23WrP?maWD~D#BLD?3eEOHr&MbF@K6;T!&SZCTMT>V<%h_E+PbNz{D1#!2rLK*&Ir3$q)t4XqN9~$;PVrY z1qLvA4E0JvMppsU3Nm5trXlIW=wkKucZw)rxufr3!!Ma`gS)ksJTjOl_kok4#~iop z2#-DY08g5X{_sx$=I&KXKYG4_3>$*UG$Ce6&n$EMYYoeAWe~VC`=>5UANaO4%GyfV z{krxYfR30+S1AK+Lwl!utZO!`CwdC&RgstNYQ4UzsKJkZxUYBgvaubpotFQKjt!dj zfX8@t0}lq(U~9pYGWQ-sT66;U$yVYT1sZuKZ+e z0Ui-JG3=NC19Z)xO9Cttgzfg>VSRH8lb0_Ap3%Zq!`b$*B--8f4N9Orfj{LiO5OYn zJ6N3(L`+>SshN5uzroPyd&)0&nE%25m+m*0nPC-zaq;VzU+*Sv6(G%QnIx=&DYd%Ad1b*;l=P^LpfC%7C0Yy21PY zEN*TjLAP&{-2?}wBbrh!jX-4o=>8&FBFy|1>)F33uxQ?pfCxj=0>tHA#^=w?wx*iW zt9!(z&xiE5A|xBeKImK6SBfvv(Xh`8XZB;?{yMTC{*d&uWCO0emKH-&T;-bZKKWTWT|g;*Igz)aG&C)B`+kr4{jSrK zB522rnII9)H6BjX)>2{0l{hzTgo|m{nH2T7CqSs~aN}*Y$esIw$Uj-7+|wH!zN^cr%sj z7h^r8?kNWEnw|OHGWW(Nuz%eaU;;K75k46zP{#88}xuA;v>YA$s;F)uO^Gvb@KbnSnN9} zdkKfaI(3+nmL-%nkz2Q=@ya>pbW)M+;D|8lWruNJe6Y!1BzOI?MvF%g(H*Bf9>Z>S zlOa6E8;WK+!ZVs-;~T|pOefRFD0l)-R#W(VONTb?fXA(;*#Ry`A9_;`!XYmC=d!1v z+0w)n{T6wN(vi)-q?zn}ptK6jQkek|@3e2CN|6&aGS|(dh zF2B(pm~r;;;*U5xT=RWmw+CO-WOaMp|9k6H_#j_bpSR!4Byp(5e)-TRr5?f`7-rfJ z^~+jG8Ox=9&bPl~kU-R)99pgSgf*$YW<&R&R4|!9>JF+t=TO_Pu5V|Q#?!8~T4N4X zA9vk1%7&~cp)X_h?1%?_rm+Ei6#=YVTIxv5odj6H>~_b*HAgG7PRC-mdWBDtP=}5Ma54*r zVPC(wbLRjk(me5el*XfQ^kEf4Yv26gZ9i&_){Y{gqY6!Q|H<5DPmi1@{s3jq z%h|15>VhaVf6I`eqf{y3jRybimwKfzk011Qm0FH=jq_C5%uAIl3&Xt6aXUQx?UiP| zECWv9SN__}Z%wb$u7{iCh3o#C6U>AKT!`D*jR$*Drk`zC^#uC#4?jHGZ5j4Do9=Kp z?s<$)kbL9Znf{q;w@3Q4x*nvMuqvLF@7UiXKzCEjjy1Q{a zTp7`Y%m|2Yv7md|qwc<`6KAEFL?n9LjMD3-bgo$`POXb#yR8^mZ{+=mr>4_ju58d% z!dRa1=uRqUG9i;NyEhSCf>ZnxNUjG|h(iHd*5B-En{(1kBxTY0qC zuQ@l^c)MzQK6Z=ZTsvIW(gB*GrRqb}Wuq%w9l`hd=Z_7|fZ5nfm+`ZtUCAkSN6XW< z64ml8%@?ioep=~0wHp*Gz7)>#qaRiptxoIx1G-eVm(}xSkFG*1a^Oow{0Z66%h>zD z;%KLvrwHGl{{%y;;&-MJgqv~^km|tWM?$$d$q>;I;hmY=_K!Wz(mAsO)~3+v^mI`y zS*u%+Zg;ykJSqpOZlOnFb(6YDj|&O+Dy;g(^Q!wS=4$1x%~&MTZ1A$OQ?BGt#c4Z3 ztuNOd$n0{*GxMX^yrumfYVjM8;M*l9q95GZ;5|Ki*Cgt{?b2Q6_4A9pY>v&n5AxjL zOwK$iHhNzvZ=wcxQrp+NCMowL`;6yTwrt;e|7}94H~jlkn=Uq5}$oz`ei^6`v=hz^pW=niOx2}SuR zd++)1-zaYC5 zC?YN#Vj9^ezQF!0BS70NJHYF>LNMf3arzfg>tCX&r&rN$x`Jxts_hCZjO?Zz*(Mr< ztq*IKepAbDS9)gfAGZHX+dL87F?^OI=KSpAV-A7e?OR^j&Lk;~3-;z&M@NAvPL=uv zpSPzhOBhriON2?-=EiLs4b4bDh1&pntR^I)Q*FN(mHfUIZuOZ*#3-?cJ*+ zq9Ca)IG8%Jk?Z)lpGeBvO5X{GL`EI>p~n{-__+Ev!vaxth(h>dPf}S#oc7Vnu2t$n zak`ho5YIs}pK;(b+O)ICs8`TFeAqFmXhzAXb%6>Wv$)R8$-Rt#>lTKRnDJlfYyKSE z-`qcvD$Sq~H>SF#8x!-Ifr~oy=K8po**Q12>!)C^xlP`x?g5u&d5y`@SzEb5hR&^$ z_VP>DXm?9O#YItzx~5wtE)Sx+Ogqh}EMdo?--t5;Y#k7cdNOx2+PYfpnx%>LAqj&p zsP|d3a%BXQbfj+7wjY}Rqco&yKeN9OPIkAsv@u%!U&KV`$;knv zgCuA;+cxF98pgeX-pmLfQ@Y6+>ibErzZryGW4{;S=F?h=q5NWq`_jamea*p8c2F>6 zw9!)%aT@S+x=ua?f5v16WV&M%r9YkBbQ3}p>x+5NzyF07*`GF@#X!KFq>NpSe`a>` zpDk^7nAF?kHt`eQ|oa{7ba*NiYsd-N&=fn z8f;vN7T0%o37x2ty9Jxsu5$D`ObM+v8LmEPQJ1*<2K~XTry~^G2)|X5Vh85|Ss8HM#A7fFTx!UgDS6F1jl!R-rSr{yKD_AJ$f@c?*f*ma?mlS=F=LC9`m^4V$g6*l>E#lRSM=K{{?DRBk~U#Thh zM$|eZwsVQQ6NnMTwR{&GhxL!K@Z$&l0~5+&t3BfzQ+3)dp5h+VTzGW|S=74v2EB0Q z(%J&E0`_}f@LGVl_pRalaqGiku`klVD@)IV>kW!(^g+-X7%$?vQyCFk6L{;4kL^IM zRA_=bS_Tyu;Pc*gT5%X6#skZIu!^TBS zZG#_4i5*o}(Cxltclv0Ut21&j13I`FNUAw^OYos~2C<{?+qPVA@2kIf^x0V(8|UuN(NsQnIeMQje-+nVZQLU~CcpPR z#Eg%w>ER_dr^N%IyN|OZ|JItF@t=6{BK{rxc;`3?hrQv47y>br$n7Ex&CZi*rkYFx z;jtuqUvHu(c22cl(vmCbuq-7N9$P9>-Soo#^{IrE?e1glMYFqx>k9T4XB}+G6FD>ZHbG4q3 zrWM|6F$E4J{WI{wd@5~l19_ia^eL+h1X$9l@@zHOA!!$^@?{d}Jswhrl9Y}XbOs?E zPe5w|?Niz4hhK?B{)=G5WB+xoM<0%uHY0^Tle{P{;1$BpovsKc|FNtQ=Tgj z8U8i;`!8TQ%GZ3Rw6IqHRumgP9z^CBizZEiHf=-kvKYqS(Kq;I-75xC1W!M65k}$r zMx@hPQOn_{C%1zML;g6%$6B4TId-~@5k<~b`Rxxu@S&Fy8xBkC8||-?>7&MYOEjHHG{1`*HN>8)z}Vbgr@Ls@O}J(~ z{L#cy8*ccPk zT&}JU7Fwr_g=eM0PX?7~1Dxl7{`s8(`xG&``lwdTg>Vq-BGNx84+FC`-hsG;XxR$% zv%miNaknV5_lz^%T;BGkkC8R4K%XNYb?En7S6o=AL#5BhaM|(*>v&)J2w)q*Wx1u9 z-KUooKavIoVFaKvjh_MT@_Q!HSw#cZ_g2n#J zP$u0P6LV0G9ITQ-ZPf$IVR-z^)%Itm;;0ICDLa{~j9j?QvF22F8unFV2n>#?AmK5)&S;kp}E1vAn$zb`u@X`vEPSr&M$VT-9Kjk4{jxMEOpi`tUpru zNJwH*Bx!-y$bSY!GCtDBF~tqSyhW`N>?YMSdY(HIF|N;4=`TF@qYHK%pMR>ERt&63 zRjW08tbejsJ}_Ki8%xz_ERUAB%~#~)z74g-4&yK5n*5b9e%!<$(cYdtkzuq}IQ#ds#=j~2j82ec;m$%1SE*dW~yjtpoumQrDC z9cr{r(7A)=+Xv~?SPE-VB7fm7`rtnUm(E#>n~DL=PxP)Wa@#(m!l-baQK;>?*C=;3 z`FPfqo@BE=yYL&Jys8~|%#Tf_c=$osMEqCR^O}kOOkCQQttX-&1E2QF{s!A`(;C9i z*Bu0(loL6vCQDAy2pLpM@Lw3s1|FUSHaanq434Sp`3%DAYV*-j;eSB9xjMD zK^IuYa-SoAQ8#T*xhhr(>#G>$b5VGVM99D~N+ZZ?Cjh z@`9?TCG6r>PPkiL;~K}*@)w0pLxZE3zMqK{JA)D2q{b%K(KJ81=wH}9uh=i|@BhM{ zaHf2*cFMV|T->hz!eY_3NMU!hHaKHUgXw-?jGf?M+-^rXQo{e@Mw!?feHR{6l~ef> zlx)SG@#s{Y03(q{hx}9U&()r`TUSi)g{2sO#Y%}p9%C);cUbv=s=7m@T>7aQ$U&nj zB;NV$YSDSw94i&B%}wFNx^gVI3NQ|WbqlvFQ{7e2YROfzI)Tumny80v2)7-jC6T=q z=^4}$ZdcAgXH=YynJ~Pss90lc>iJmMrP=%9i`)<%>x20GkxvdZ?DLmSwoeh1?8_dL7O9`ez&n91EOQkLqKe zg0ROSK*w`Gx}_=nVOUuku1?3}#ohFl4Kg!F{OSq0YQsmB!6%&E@nxH%o^U}BDkfg2 zw`yX`6TE1wO@F5VbFT>IK4z z{DK}^X?&!a8ZsTFeePX8Ku5B&2^<%ABT;L{^v}_qcgdbr4LP7fOLTSM*TvO2A{d)~ zKnOA?7#^nG9xct>cU}h;^F5Rg!wU0dSQGhhWB?@Ik?Wn>WT>A3_`iavka8v zhU)8!1$qY6HW)`fF>Q@`<+vKG=eVjoJcBKY)a?z|_KV}+;zkF>jxO#A@C^`DFqPc$ z+K6tbxX5u8K-qJcdcJ)G{7XroOzcmHyp4VSevoQ@LAk$&HsZJS zDr1_OCo7Yxu<2@j=WCbM3;45#O1b$x%0&G^y?7!Cy%zw=K4Oz3>|SHMiNA2y;4o6i z!_L8qY&9zvfV`#>{zXW!$HtG{BIYM1Tpw3*w4AzpwTJ3t)yRbRj2z<~Z&$GS-hPR) zek|7{2mnDo^X8103M*DXr?DopzGw%yJjul1Qir4eE2n-#T@#;F-ZDIp1p^*csNuG6 z{nW&|*k)oclh(e__YF)i?b6-*IPqe01Xs?RNU7K(Uwj{#EKeNC72q6tGCT~;UQ`jF zJZjUTQKRL;JPKarCER5`<`<_`%LJ4U$U{jt&wT2iOirrK%}CpQGXPB|{eTmcIHWC< z?i^IbnY>)69NZ?uUu{^2nfOFz6+50Q0IaC-PvHXU%o$&&;-)IGOUL_Cm`w08yfb9G zSBsz>Tn56GE$J(j;j2av>QNFRi!C4VQr=?X`&X*p<=h>Zx?PR9CNsvCfr~>RP3NIe zMhy=LiOwWPTEOa>w3snDe87V1$wy+{im=_p7%FU=geQUR*1jhFD(B`m8v?C1KDM%> z_qKP-!`K^tB0r{Qy-gNLvu}L%DPTq%$Gn@4xa3c`PugTej&>eI<##Wn)$4x6_Tp9Y6w2DP?8GRN_~W;3_)V>q(eUTaj&A#bkqQbxzo0m6I!t;QL~C{zyyJ z&PKZK(qep1igHx+$SV1Uu;Wpi86~HsH&Jq{@9y3g&4umLnWoEco>@IJ8yYHrtzx6R^3(gp1-6WY{&XkP0V5f#!ScfEiwAehRvkiFns%pgTF3I~$nEt6Ngy zenU>d67)@T0aRg;G!;bo1#RJ0;CJyj;2d2HP)oZ5&%kz2zR#%Dp?h87Yx`0Lr$y`j zf$H|ZCjO7x5@1ty%HZS7gL4~1-T92lJ!9mjsS7t~0>Y6Cqe zQ4^BG1#YI&*5sGnqq$H89_7lI*8}hSE4&~hons2Ss~WFCN+9suD+=qtxxpXL^d;vS z1Ga`WmOJ?w!0FJ*?lVK<`F7H*iO7m-$#avZyDsOGIkl^lDcht^pXC8d%teq&9rs+L z1#on??jBK-XBQ~+(4+l5su!UrbkrF0k}G8n5Ws#jHo|iO{%7>jI-nd4pMN_ID#xXh z7d3P!@ut0plUnDC(J~ksV9`M~;L+CR(Od|C2Qb}~^L!Ea=nP3X09!zC{%{cm`f))% z7qB_eRPQAkC+pCLGibj_uFPT7eSnm#@>$on2#$Mg!E8atrK2gW3fi=H`!xT+cypAj zNku{X=K&Do1<7caawerowXl^nkhxO6s1&FI?>>_}J}|i`O52P&K|%CY{V~TJ4=;J2 zUshnTxm+1Qd7jtkm2u<}(P0Uv&cj{+uoUIN0Zj@j?K|mAL`p_YV4a75)DH!- z_sZPM&s~zS4FC^)Pz&4Tf{hhu&z^qe*$mtI2sg6rnUkQkoB3t+8c!$dU+>RQLcgDn z(@?r=baknHz%D$yB<^6Bk8Hsh2}Il-3Cyz3Y{Hgix=9}+CU_F?d34^s?tr`e=HpS2jd24zw2wB$=ic+&mS8EsZ`czO~d-Z zGy2;UF5&zvCJ?aEUFUnB-AcD2qN64C5W&FYd?}*+y?c=U~C%`4GZS zX91<1c%sD}edn@tB$<`&j+2(elL+Q~R;C77O94s%vA|$Ghrh*EcQaORAj2 zU?Wu<4?A)qZEMT3If+A}yEtpJ%6MNrPNfM1AQM2G0PO}m+#Ym+0J0fINq)Q+DO8qO zG8)ysRq`Y@Ms~%@uPIy_5-muIAbL_EN3SM&t%gXuMlhV81Alhb7tBIjA_OQP;-jdd zpa8tb%u1(pqznX}qjkday05HIoFiW4(7aNikfL!R_}gF|qh&udwqFLZ+-Wh(Vvl$} z7N6_olA8AF>^%?Rv`cA8+N=8h74>r0%^y(K$SOcxhuEd$ju&art0`uyfvKvTc;cRE zwjbsU*y3OMYpAqL)pk#{o-B)fJw&Jbo`j%+@l}S9WQY@g`Km<(RT#hiD)p_3>_&r? z_=DA;jDTba7jx{I#cZ;07?eFbKyC*5iZ_W{N#b3fI0)0ASOZQ_|D=Ts1*dY~`zsqH z8#1f*48B9;uCKZygVQK+n3sdtDt&_%fx%?IcHA=kFZkT;^sw5ks(TnVjY8=YXx&cR zhU??M$>tI@uDsTSb`Qf8naP*N@1Ff#so!6!zFM#c)Bd$MGb%i0Qn(8SH2Sa1+-p7w zj?5INiOGFd*2q^m=;j}&bA#Sz=k@7HZxA^XWG^yg=Ek!jed05bj@dwfVBa%V=18Z| zXVK>ky$x6xy=448aWh5YQ`M?=i?oE@Pg4cJ~HJ@l5H6fBAXumSI!g})^brL8>N0DuRAav9K?aH#)Q9AqJnAP*D zqHxyHaT>h&xE=qK>Er}gdKUOw=(uTXbYio;hppR#DB$$BmZj&>cd!=nL+5o{-Onr9 z4d(K-_kJ9Ec|9WzH8#Eq@!?g_I~d|OLl>O)vpe-StEm+IbOeVPqnn_-`L>9WKC<(J zdfgIBUW>HswxzQbR0KryT6Vwo4ytr$%R^Qva^6m=by_If8ZlC$?TDy7nCq4o2#%tSy3)l{ZL)*HLt~*sRhC@t6I*Xd~*01 z3ZHgBV9kES`ia+M?Iq#jVb{r9j+8FPjZ){@qse%FMD)0}Z2i7EJ$_K}9{Q>V+^H1*V5BW2F98yN(;^LU-gCAovlevtC!gx)<*WA> zsw>Vel@-D#_fYU7m-?NheR8|avEh&H!}XX2UZEMP?VwRVVo_I$bn`k-ONMOvR$W#g zzV4pY_@Z#To#2x{5)H?fpG9b}@&ALzzyjN17*q(_d9Bp)Wa9J&yvQ8B(sc+ItoS#p zNh?qE(W}y}&P-usL*igrOC#CfgZlYf7$yN$lfkv_`0EpOkI8%g!I?7OBX{D0#~ zbnTNB5KbHSU3ToQeyb$%PUg8Q5zB`BU5xhV&_^yh1(rx40w)t3;`ktPRr~7Pf|psy~X^B zKJv$2Bm8;4r4?&k-+ZOpkE+=kONw#G4O;K680pL?LDI#^7N7bf579}53>o5*gudn# z>1N_~>Ki+ov+sd043rFF@A`~Ei#N?f9e;L8f)Y6<+%0Hk2(tz6Y_I_xtYBfm8Dc7J z37`db=jH46_{-?*sE>DK&aJObp)AW+jU^cmRGb z=vCZ1;#T?^)O3Guco|U^+@1D%GvnL0R?Q8nMNs*M=(x^|aCOzXk7i{1Sq4LIi<%Qi z34Gc-{tWT%m?3H%^vZrnt#$A^3d-)t3zT@(cQI4_xhqDwtbj#2G6Aa|2!_UcTfs~h zOO!vIlobZbDsflTcx%p3_iVgQPn9X8$Otg8FtjntB_gBUW&NvIG^1GiE=u42tb$@V!bYv+}dpY&$|LVR-`;CtEtPCVvz+up{x1OR0Zh)jSU!`hIMG5i7P#>EyqeVlcz@B-EqSK-Y$g^aJT%u(2a8@(0#8HG^VSWAl5jyB8n{^hTbx`R`2m+;$!uEZ%D^j`S;Vy0Qi_Kq`X=0@B^$Q z;35I-(?9ZnJ$c*^bT`LZ;{}^5!LJYSY43y=rFd*4wAiaZ`V@?18AMdDoZzos0Oy$a zz3`ETW(~)96W!p6>I@;pd#vhL4OVMiPmIGU4cc=+7Y+CYS}X=Z1E?vL zGWh^9aD-g$Fih*_UIy|Qbls~rl#Iki)$0K(3IHpllKv2@`|LJD0?%EIwR!qCFS!uz zbZ|<)!XY=8e4@sjDnwABrgJ3#FKf(>Ls-=TQ6zjc#pI5*;j_# z8A`}ivjT8q;5sDBGXOP_{we!z(*t4n;U0w5Ho6cp4Dbiwq{I8K$-}q?oLqC(egU}$ z+r*cG)Q~YigLoX2MV6*vZCv>TYvb~ZzXoIeh1)U(3w=k&WENjZoO%b4=U|;wjD+Bp zDT1SSWKb|^QLzjBU)b27!4H_FHfx!CJTCHp98QT{5-(Tb6c%t=V1s8|9-x!qn04R;E@!d zHQibTKqPq03$m^Eqg(aoFB!CVD>!(nJQJmVwttmN0x}tVTD>XD>C+pQrh{v!$LE)L zLjESGcYHz}dW{xTHUWfm>PlGLgAoTR+s^(PBu`+AsQ^B**8-@cNuV}qkOv4ft`i10 z3(csGLjlbSffE%MPe#GWpitrK)lnv}$yM@=VwitFIt<=Z_uyhX--RZ^Hmy5&+I+fj zx23T!9=u9DlMkn=B3LR1S@wM)C6ot~a6s(aSAA@XC0X%ue6i570!jQf#Rh-6i)3}} zU}hh&g{@rvaS-iWI;_;|rxqi`&*M#RRyZJC+!_u@dcJ#&n;<_7g2`2~$gPM)Wm! zI_)l@6e#|Xg>q@ET*M&&ht?M!vzeIHg?hDYI>YAv4!Ahw=chA{bV>agJPqPTTZnNW zhA#ayshSQq101)xt+f7r^Be#g=zVc#D!t_w!|+2d-dl2df`A@j9u@5O5I{m}Yjk#U zUE)TdB9U|qLB0Po>^s5uo50in=@>U8n0T+JBZ!5JiR3-fjbr>pTM z8dQ+Ajste=5iFg9EAZyW%EP6169nig(HKTfbqs<1w1@AajY0go%L8Bq~PYb zZGIP%>m+KI1W&`i-i2?l7EfYq2yo>lU@nXD>eOWiaQBO&!v^=p{+woCvVI{nW=pI{eS*`!I`=z)!ly6VHGhpqL=EhV~^Icv`W zf4-d#SZol20r+Srjk_5mRwX9l1Q2k;@!%4XRy2fLo)u&i%+i50f}tR6pNjgA{PfA| zamjj5`~S0F4oS>7a|&cb$_EZJ->O_=IqMz(aNGV@S~x4b0z(h2@;RTHSG>BhDdO(` zM+~>$|sif|z1M;1GTg^8m2%VAWHC+>lkO9Nh{9DKi(7*X3?x|m3 zF?bOR0+N=M-{MqIn;obw0c#{#?H;1*5wutcoW@G;0W&@BtIvZm z)}*Yn$H8EF0vHM4lAwoOHaocjVqZ`4@NkOuI1_`f`E-1q`@WfN@R~62d?2ApyO?AV zE{vbQ?Ng86POy^OV?}HgY`5Q9RJ*m{ECcc)gr$U1cLDo5Nad`wSQc^;tvmFPa-C{ zU56mqc!b=Tzp{e_I?s&y9G(4|7{O&Qzs$~MJ9lkeIr_s+_lZ!Y#yE32c{6BsbvLzkGOQ1USw+_$r^p;I}kNoy^6E*9vD zS{;&*j#{Mdc?&h)Kuwl^wBcPH3StkL@S9U7*hrBAh=p5q*zgHvC<|M=Dn~7OGK{H4 zrhmeStb$OSdxo~Hc6-H*wr1{DVygi1ePT7PI`zkjD)q5*lLo@Im35rXbaCl-W*;C^ z0hH8=m7SI2J8D2~{TJ>oBI)COSx(%Ne8vVN#@uS;m%1>C#*wE^)SR9^fOiA2G`lyK z+50&NcEAXjesf%W!x&rism~{GRA{&Xs?x{&T-+>omeUiEOu#5`h0ecIG+31e9-heu ztks3nae||0FxsfYJYSy#Olom->Nx{_;am*R2v1FvGoZq-g$CZ+-`IExUQutDTlCWM zdTA!9X{#Mb2G!a9<~?^s2P#-Fn${NB#W9&8>^MitkY*oTmfx!&acAF~oCt>XoVpzk z%3Df?w%;9N#U9c`-iWfHZ-Kq`uNk$#)(mDWRY@LeF zc|3IQ)E5+)0fv@?P}39N3rhk{inHOP){C#1V!x(CGWwK@mt6L;7`l3$Uu`b-Z%mm- z(Tq%Prn*&=?{%M|)_(6uG5s&CYel$5gT!sS z^8O*v5ffZYAQT(1Embn(5aUnF{xLn-pV$wc{$I!pvgX=(h}zC}T=nBm+#IBGW7mhaV0S~V`3 z@P;{Do%MDMTTp9&eiSIMFaiV+JDYBSJt_O$>gjQ-hjFkjzzKdTNQdGS1b8C&*g@S* zZap^kdvV#Qwa~8D1EW`bn!;ord{k$`KaR!86z5P6z2j;ej_$eqmm%?RvVQefR#gQo zaLoKwWa86e%+;_jZd#WD=(H?TDpqOp{y$HbQ~c7h0E89Q^_Bs^u-Drk3MFm>B%%@! zFrK_GCOBLce&4Mb3qo~V9F?`;hqVVEx)$~Ho4h+`moafVd^&Xb#?mC16VL?$rR5x( z>mNH}!gV!o9k4=5#s)#%d@_`Qm;Uw^D>-#CV-}#2LA&yvjRusePuyP56_>OAHy_w? zLF9U1X=cobFS5$e@=%@aPh4aa#d^UVJ;$hJ`4AXld3~3Q^f(yya#bE^$6G@{UiAR# z-apq$y^pm=S!L7Ct-?JCi`Q9;`|^WFK(bv_Q%B$a)#U3!z9L4zc9AF6t&1_s_~pIs zyKaC^G6KVZ#fr;tfbImd=i#i(s4zx+WTC-D*bl2?!TZ_Yi#aW#J;jnDYp8++*dqy7 zg-zArn2bxvK@fkro7rFo!G$@+VKX(OS77I%!aTQpk~ZCG5{SV%80k80hXUT)u$DL& z$mVBy_??G$MVr}_NYvJJWnVPmYHPgIL##c8+C5Ump9>xRlMI=e`WNtz09XJ zS4D<;V#jrS5qR<3uzYe9FDQQ2i9WyRJlyNWN%QgW=C5PiLU|FSnS}37S?E#RF_d_C z--$6snc7)J61UCVnS=~^jXl;J%+1lo)qn&Rl!3GukzS`UhDOHxe0+pi9+7ypmpucn zwylL7x*HhX+C{=owZ^W-f;IXCVchJkLsp|N5>w1whOFU)F z(5irNhyeQ^h;7-zP>4kFav-wR7YVSj1dx_Y3M*+-zqUh6N`NW ztWaOGHYteP?j?)k2E*CCbEL$8d3lR$dU2K%qqGWY}BZ zKWYrM4q!}Y0io>=7u+V;zPgwUNZy*sWa&)$)`zhj3w;jf@V;iSg=@aK*M4LpKwWm! z2*XBOM1WmIfWpm?2@QwDTXVB|E1R|!7QJNppv!(2^kYDZ3pQNjpPTn2rHpg2Mt3ot zI;`6J%LArk#-VlYdKImVvC!I=8{tlT#Mhm|d^=`-I4Iq$$zmwcHeq{hDO#DR7(O&@ z4CEr94q7xEDSROnpWiLBQpZd9$EmT zQK5c+;WhmonSkx@PW@$ULZ9P>Kfl^o1B}CQ6T-*Ba3v0wkTuG+0r*D?k7ISfCo+*Q z@k9Fy_1Os&IzqphZH!eN@?9VQdCJ@XMRg%0CQ+vAvIh@w}L+BOvV`Dq_LKlLMp zU??!TSqs$)BW4RglT_(*_lP3~Hb!z?5hF5@lzxvDHu=XacGN4GCctLO)`iy*5I#YL zT%8z1zj&`vIG|%gtYBcrcVLVxv-?Iza?QSp$8$%%B+Q3b90KJfy(Z%TpG+6|s+vTI zJhGo&0`WW&GDNE8LnJz|;Q@HhvSHV)$uhNTpEd&q%xX1Q<+jFteIZH%`<}}vhOTR% zPHg+3;fm8c^K%8-d!qjCW7Ob9piyu`dD%!bmC|+ED8cAreo8=AM?&agA@yApcCy5y zDs6^}#KWK8wDgO2n92}OHrL3EW4^yVt=P$?@v^n>>0LGWL4SN;*j-A2gVoi-J+MjP zRggk|?unC=*Y=X=wc*cbaI1SOrNv7x=#q0LzZ8Hc67{d)7SJnks1h5@OGN8ho`weW z_f{t#M6r*jZu(X zNB~jWfIbEFP=9lU8^$P?q1aW+mI1Wnst$iB8R1(i_Vnq$dgH_w9{Cc9I*z#Uu=j_F&}qqQ7q zx7K8EzJx%7&1j_uZw_(A!tg9$Scx;bUp$r|Y=IPzO++h%&$b|QYoRzDDm!*Z@h_A)8@7-J{r$dn|2*R3bxF)EG>e) zwy3dfQS>QcgoTC@b0kv!Y1GMve_a;jWDMCW$Yw^h|C;Y1KMOAqoCQqeQvD4&=i&D!ND&0R?XÇHS?RntQ1I!I9``-y(E;j&0z9)hLcgCb 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 S6#*Toaz$QMF6*-4!~X+pLLrC% literal 0 HcmV?d00001 From 922c7f85347e5d467d260f5fe7e3f29de3f54ced Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 15:52:11 +0100 Subject: [PATCH 108/130] chore: add BEP's and sort Signed-off-by: blam --- .github/vale/Vocab/Backstage/accept.txt | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 9e7cf7aa98..d933d2889a 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -9,6 +9,8 @@ Airbrake Airbrakes Alaria Alef +allowlisted +allowlisted Anddddd Apdex api @@ -23,6 +25,7 @@ Avro backend's backported backporting +BEPs Bigtable Billett bitbucket @@ -95,6 +98,8 @@ dockerfiles Dockerize dockerode Docusaurus +Dominik +Dominik DOMPurify don'ts dynatrace @@ -119,6 +124,8 @@ Firekube Firestore Fiverr Francesco +Frontside +Frontside gerrit Gerrit gitbeaker @@ -138,6 +145,8 @@ Hackathons haproxy hardcoded Helidon +Henneke +Henneke Heroku hoc horizontalpodautoscalers @@ -167,12 +176,18 @@ jsonnet jsx JWTs Kaewkasi +Kaswell +Kaswell Keyv Knex KPIs +Kuang +Kuang kubectl kubernetes kubernetes +Lainfiesta +Lainfiesta ldap learnings Leasot @@ -413,10 +428,3 @@ zod Zolotusky zoomable zsh -Lainfiesta -allowlisted -Dominik -Henneke -Kuang -Frontside -Kaswell From 3ee310ce1ff48e4c8642718ea8f09c20a67af249 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 15:54:51 +0100 Subject: [PATCH 109/130] chore: fix duplication and bad merge Signed-off-by: blam --- .github/vale/Vocab/Backstage/accept.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index d933d2889a..e6380a17f0 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -10,7 +10,6 @@ Airbrakes Alaria Alef allowlisted -allowlisted Anddddd Apdex api @@ -99,7 +98,6 @@ Dockerize dockerode Docusaurus Dominik -Dominik DOMPurify don'ts dynatrace @@ -125,7 +123,6 @@ Firestore Fiverr Francesco Frontside -Frontside gerrit Gerrit gitbeaker @@ -146,7 +143,6 @@ haproxy hardcoded Helidon Henneke -Henneke Heroku hoc horizontalpodautoscalers @@ -177,16 +173,12 @@ jsx JWTs Kaewkasi Kaswell -Kaswell Keyv Knex KPIs Kuang -Kuang kubectl kubernetes -kubernetes -Lainfiesta Lainfiesta ldap learnings From bfd441b9d5f4dbba84ca30749e0822d38df110bf Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 16:05:28 +0100 Subject: [PATCH 110/130] chore: fixing bad changeset ] Signed-off-by: blam --- .changeset/slow-moles-sin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/slow-moles-sin.md b/.changeset/slow-moles-sin.md index 140d9e7435..02e1952312 100644 --- a/.changeset/slow-moles-sin.md +++ b/.changeset/slow-moles-sin.md @@ -2,4 +2,4 @@ '@backstage/plugin-circleci': patch --- -Making workflow a link +Making workflow a link From e6b80909385fe5b7ab03c32e95e43d0f8d9ea4d0 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 16:50:42 +0100 Subject: [PATCH 111/130] chore: fix yarn install Signed-off-by: blam --- yarn.lock | 170 +++++++++++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/yarn.lock b/yarn.lock index 259b321cd5..04738e5b2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,7 +3362,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -3702,7 +3702,7 @@ __metadata: "@types/jest": ^29.0.0 "@types/minimatch": ^5.0.0 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/npm-packlist": ^3.0.0 "@types/recursive-readdir": ^2.2.0 "@types/rollup-plugin-peer-deps-external": ^2.2.0 @@ -3804,7 +3804,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@types/jscodeshift": ^0.11.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 chalk: ^4.0.0 commander: ^9.1.0 jscodeshift: ^0.14.0 @@ -3827,7 +3827,7 @@ __metadata: "@types/json-schema": ^7.0.6 "@types/json-schema-merge-allof": ^0.6.0 "@types/mock-fs": ^4.10.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/yup": ^0.29.13 ajv: ^8.10.0 chokidar: ^3.5.2 @@ -3870,7 +3870,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -3968,7 +3968,7 @@ __metadata: "@types/d3-zoom": ^3.0.1 "@types/dagre": ^0.7.44 "@types/google-protobuf": ^3.7.2 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 @@ -4025,7 +4025,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -4049,7 +4049,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/inquirer": ^8.1.3 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/recursive-readdir": ^2.2.0 chalk: ^4.0.0 commander: ^9.1.0 @@ -4174,7 +4174,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4266,7 +4266,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/git-url-parse": ^9.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-markdown: ^8.0.0 @@ -4318,7 +4318,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4347,7 +4347,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4375,7 +4375,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-ga: ^3.3.0 @@ -4401,7 +4401,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.10.1 @@ -4445,7 +4445,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/swagger-ui-react": ^4.1.1 cross-fetch: ^3.1.5 graphiql: ^1.8.8 @@ -4480,7 +4480,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4656,7 +4656,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 luxon: ^3.0.0 @@ -4718,7 +4718,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -4770,7 +4770,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4862,7 +4862,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -5560,7 +5560,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.25.1 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 circleci-api: ^4.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 @@ -5593,7 +5593,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -5625,7 +5625,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.27.1 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.27.1 luxon: ^3.0.0 @@ -5687,7 +5687,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/highlightjs": ^10.1.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 highlight.js: ^10.6.0 @@ -5720,7 +5720,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -5751,7 +5751,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 jsonschema: ^1.2.6 msw: ^0.49.0 @@ -5792,7 +5792,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/pluralize": ^0.0.29 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 @@ -5834,7 +5834,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" express: ^4.18.1 msw: ^0.49.0 react-use: ^17.2.4 @@ -6017,7 +6017,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -6048,7 +6048,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6078,7 +6078,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6108,7 +6108,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6140,7 +6140,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.3.3 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/sanitize-html": ^2.6.2 classnames: ^2.3.1 cross-fetch: ^3.1.5 @@ -6173,7 +6173,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 peerDependencies: @@ -6202,7 +6202,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6238,7 +6238,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6272,7 +6272,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6304,7 +6304,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6336,7 +6336,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6366,7 +6366,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6398,7 +6398,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/lodash": ^4.14.173 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -6428,7 +6428,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/codemirror": ^5.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 graphiql: ^1.5.12 graphql: ^16.0.0 @@ -6484,7 +6484,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 graphql-voyager: ^1.0.0-rc.31 msw: ^0.49.0 @@ -6538,7 +6538,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -6572,7 +6572,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.26.0 luxon: ^3.0.0 @@ -6640,7 +6640,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/testing-library__jest-dom": ^5.9.1 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6696,7 +6696,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 jest-when: ^3.1.0 msw: ^0.49.0 @@ -6784,7 +6784,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cronstrue: ^2.2.0 cross-fetch: ^3.1.5 @@ -6846,7 +6846,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6897,7 +6897,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6926,7 +6926,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6955,7 +6955,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 p-limit: ^3.1.0 @@ -6988,7 +6988,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7042,7 +7042,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -7203,7 +7203,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -7290,7 +7290,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -7342,7 +7342,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/mock-fs": ^4.13.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 command-exists: ^1.2.9 fs-extra: ^10.0.1 jest-when: ^3.1.0 @@ -7556,7 +7556,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@uiw/react-codemirror": ^4.9.3 classnames: ^2.2.6 cross-fetch: ^3.1.5 @@ -7761,7 +7761,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -7794,7 +7794,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7825,7 +7825,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/zen-observable": ^0.8.2 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -7893,7 +7893,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -7923,7 +7923,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7997,7 +7997,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -8109,7 +8109,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -8140,7 +8140,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/color": ^3.0.1 "@types/d3-force": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 color: ^4.0.1 cross-fetch: ^3.1.5 @@ -8175,7 +8175,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8243,7 +8243,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 git-url-parse: ^13.0.0 @@ -8356,7 +8356,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.2.2 "@types/event-source-polyfill": ^1.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 canvas: ^2.10.2 cross-fetch: ^3.1.5 dompurify: ^2.2.9 @@ -8418,7 +8418,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8471,7 +8471,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -8530,7 +8530,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8558,7 +8558,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -8633,7 +8633,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 zen-observable: ^0.10.0 @@ -10546,7 +10546,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -10569,7 +10569,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 msw: ^0.49.0 react-use: ^17.2.4 peerDependencies: @@ -13491,7 +13491,7 @@ __metadata: "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 "@types/http-proxy": ^1.17.4 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/serve-handler": ^6.1.0 "@types/webpack-env": ^1.15.3 commander: ^9.1.0 @@ -14841,10 +14841,10 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.0.0, @types/node@npm:^16.9.2": - version: 16.18.11 - resolution: "@types/node@npm:16.18.11" - checksum: 2a3b1da13063debe6e26f732defb5f03ef4ef732c3e08daba838d8850433bd00e537ce1a97ce9bcfc4b15db5218d701d1265fae94e0d6926906bec157e6b46e0 +"@types/node@npm:^16.0.0, @types/node@npm:^16.11.26, @types/node@npm:^16.9.2": + version: 16.18.12 + resolution: "@types/node@npm:16.18.12" + checksum: fc3271182414f8593018ef8f00b4718116a92f463f619081bd399d9460e7861e1dd7eebc7cf94c23567e418ff397babed077011711aae8d47171b5a81c5bd71d languageName: node linkType: hard @@ -20874,7 +20874,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/errors": "workspace:^" "@types/fs-extra": ^9.0.1 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/puppeteer": ^5.4.4 chalk: ^4.0.0 commander: ^9.1.0 @@ -22194,7 +22194,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/jquery": ^3.3.34 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 @@ -34251,7 +34251,7 @@ __metadata: "@spotify/eslint-plugin": ^14.1.3 "@spotify/prettier-config": ^14.0.0 "@techdocs/cli": "workspace:*" - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/webpack": ^5.28.0 command-exists: ^1.2.9 concurrently: ^7.0.0 @@ -36275,7 +36275,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": ^16.0.0 + "@types/node": ^16.11.26 "@types/react-dom": "*" cross-env: ^7.0.0 cypress: ^10.0.0 From 3264529162871c77a6b4c40b8499702e18834518 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 16:55:59 +0100 Subject: [PATCH 112/130] chore: fix yarn install Signed-off-by: blam --- yarn.lock | 160 +++++++++++++++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/yarn.lock b/yarn.lock index a3e4f754b2..83503fb359 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,7 +3362,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -3652,7 +3652,7 @@ __metadata: resolution: "@backstage/cli-common@workspace:packages/cli-common" dependencies: "@backstage/cli": "workspace:^" - "@types/node": "*" + "@types/node": ^16.0.0 languageName: unknown linkType: soft @@ -3702,7 +3702,7 @@ __metadata: "@types/jest": ^29.0.0 "@types/minimatch": ^5.0.0 "@types/mock-fs": ^4.13.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/npm-packlist": ^3.0.0 "@types/recursive-readdir": ^2.2.0 "@types/rollup-plugin-peer-deps-external": ^2.2.0 @@ -3804,7 +3804,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@types/jscodeshift": ^0.11.0 - "@types/node": "*" + "@types/node": ^16.11.26 chalk: ^4.0.0 commander: ^9.1.0 jscodeshift: ^0.14.0 @@ -3827,7 +3827,7 @@ __metadata: "@types/json-schema": ^7.0.6 "@types/json-schema-merge-allof": ^0.6.0 "@types/mock-fs": ^4.10.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/yup": ^0.29.13 ajv: ^8.10.0 chokidar: ^3.5.2 @@ -3851,7 +3851,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" - "@types/node": "*" + "@types/node": ^16.0.0 lodash: ^4.17.21 languageName: unknown linkType: soft @@ -3870,7 +3870,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -3968,7 +3968,7 @@ __metadata: "@types/d3-zoom": ^3.0.1 "@types/dagre": ^0.7.44 "@types/google-protobuf": ^3.7.2 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 @@ -4025,7 +4025,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/prop-types": ^15.7.3 "@types/zen-observable": ^0.8.0 cross-fetch: ^3.1.5 @@ -4049,7 +4049,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/inquirer": ^8.1.3 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/recursive-readdir": ^2.2.0 chalk: ^4.0.0 commander: ^9.1.0 @@ -4085,7 +4085,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.0.0 react-use: ^17.2.4 zen-observable: ^0.10.0 peerDependencies: @@ -4174,7 +4174,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4318,7 +4318,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4347,7 +4347,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4375,7 +4375,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-ga: ^3.3.0 @@ -4401,7 +4401,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.10.1 @@ -4445,7 +4445,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/swagger-ui-react": ^4.1.1 cross-fetch: ^3.1.5 graphiql: ^1.8.8 @@ -4656,7 +4656,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 luxon: ^3.0.0 @@ -4718,7 +4718,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -4770,7 +4770,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -4862,7 +4862,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -5560,7 +5560,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.25.1 - "@types/node": "*" + "@types/node": ^16.11.26 circleci-api: ^4.0.0 cross-fetch: ^3.1.5 humanize-duration: ^3.27.0 @@ -5593,7 +5593,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -5625,7 +5625,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.27.1 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.27.1 luxon: ^3.0.0 @@ -5687,7 +5687,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/highlightjs": ^10.1.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 highlight.js: ^10.6.0 @@ -5751,7 +5751,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 jsonschema: ^1.2.6 msw: ^0.49.0 @@ -5792,7 +5792,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/pluralize": ^0.0.29 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 @@ -6017,7 +6017,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -6048,7 +6048,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6078,7 +6078,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6108,7 +6108,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6140,7 +6140,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.3.3 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/sanitize-html": ^2.6.2 classnames: ^2.3.1 cross-fetch: ^3.1.5 @@ -6173,7 +6173,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 peerDependencies: @@ -6202,7 +6202,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/recharts": ^1.8.15 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6238,7 +6238,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6272,7 +6272,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -6336,7 +6336,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6366,7 +6366,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6398,7 +6398,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/lodash": ^4.14.173 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -6428,7 +6428,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/codemirror": ^5.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 graphiql: ^1.5.12 graphql: ^16.0.0 @@ -6538,7 +6538,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -6572,7 +6572,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 humanize-duration: ^3.26.0 luxon: ^3.0.0 @@ -6640,7 +6640,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/testing-library__jest-dom": ^5.9.1 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -6696,7 +6696,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 jest-when: ^3.1.0 msw: ^0.49.0 @@ -6784,7 +6784,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cronstrue: ^2.2.0 cross-fetch: ^3.1.5 @@ -6846,7 +6846,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -6897,7 +6897,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -6955,7 +6955,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 p-limit: ^3.1.0 @@ -6988,7 +6988,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7042,7 +7042,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 luxon: ^3.0.0 msw: ^0.49.0 @@ -7290,7 +7290,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 lodash: ^4.17.21 @@ -7342,7 +7342,7 @@ __metadata: "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/mock-fs": ^4.13.0 - "@types/node": "*" + "@types/node": ^16.11.26 command-exists: ^1.2.9 fs-extra: ^10.0.1 jest-when: ^3.1.0 @@ -7556,7 +7556,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 - "@types/node": "*" + "@types/node": ^16.11.26 "@uiw/react-codemirror": ^4.9.3 classnames: ^2.2.6 cross-fetch: ^3.1.5 @@ -7761,7 +7761,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -7794,7 +7794,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7825,7 +7825,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/zen-observable": ^0.8.2 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -7893,7 +7893,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 rc-progress: 3.4.1 @@ -7923,7 +7923,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 classnames: ^2.2.6 cross-fetch: ^3.1.5 luxon: ^3.0.0 @@ -7997,7 +7997,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 msw: ^0.49.0 @@ -8109,7 +8109,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 qs: ^6.9.4 @@ -8140,7 +8140,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/color": ^3.0.1 "@types/d3-force": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 color: ^4.0.1 cross-fetch: ^3.1.5 @@ -8175,7 +8175,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8243,7 +8243,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 git-url-parse: ^13.0.0 @@ -8356,7 +8356,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.2.2 "@types/event-source-polyfill": ^1.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 canvas: ^2.10.2 cross-fetch: ^3.1.5 dompurify: ^2.2.9 @@ -8418,7 +8418,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 react-use: ^17.2.4 @@ -8471,7 +8471,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 @@ -8558,7 +8558,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 lodash: ^4.17.21 luxon: ^3.0.0 @@ -8576,7 +8576,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": "*" + "@types/node": ^16.0.0 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -8633,7 +8633,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 zen-observable: ^0.10.0 @@ -10546,7 +10546,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/test-utils": "workspace:^" - "@types/node": "*" + "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.49.0 languageName: unknown @@ -10569,7 +10569,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 msw: ^0.49.0 react-use: ^17.2.4 peerDependencies: @@ -13491,7 +13491,7 @@ __metadata: "@types/dockerode": ^3.3.0 "@types/fs-extra": ^9.0.6 "@types/http-proxy": ^1.17.4 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/serve-handler": ^6.1.0 "@types/webpack-env": ^1.15.3 commander: ^9.1.0 @@ -14841,10 +14841,10 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.9.2": - version: 16.18.11 - resolution: "@types/node@npm:16.18.11" - checksum: 2a3b1da13063debe6e26f732defb5f03ef4ef732c3e08daba838d8850433bd00e537ce1a97ce9bcfc4b15db5218d701d1265fae94e0d6926906bec157e6b46e0 +"@types/node@npm:^16.0.0, @types/node@npm:^16.11.26, @types/node@npm:^16.9.2": + version: 16.18.12 + resolution: "@types/node@npm:16.18.12" + checksum: fc3271182414f8593018ef8f00b4718116a92f463f619081bd399d9460e7861e1dd7eebc7cf94c23567e418ff397babed077011711aae8d47171b5a81c5bd71d languageName: node linkType: hard @@ -20874,7 +20874,7 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/errors": "workspace:^" "@types/fs-extra": ^9.0.1 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/puppeteer": ^5.4.4 chalk: ^4.0.0 commander: ^9.1.0 @@ -22194,7 +22194,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/jquery": ^3.3.34 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 @@ -34251,7 +34251,7 @@ __metadata: "@spotify/eslint-plugin": ^14.1.3 "@spotify/prettier-config": ^14.0.0 "@techdocs/cli": "workspace:*" - "@types/node": "*" + "@types/node": ^16.11.26 "@types/webpack": ^5.28.0 command-exists: ^1.2.9 concurrently: ^7.0.0 @@ -36275,7 +36275,7 @@ __metadata: "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 - "@types/node": "*" + "@types/node": ^16.11.26 "@types/react-dom": "*" cross-env: ^7.0.0 cypress: ^10.0.0 From e7169461033c205f5554fb2f2598fa05b825fe1a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 15:54:19 +0100 Subject: [PATCH 113/130] backend-plugin-api: separate out hook in addShutdownHook Signed-off-by: Patrik Oldsberg --- .changeset/bright-plums-cough.md | 5 ++++ .changeset/green-guests-flow.md | 8 +++++ docs/backend-system/core-services/01-index.md | 29 +++++++++---------- .../lifecycle/lifecycleFactory.ts | 12 ++++---- .../rootHttpRouter/rootHttpRouterFactory.ts | 7 +---- .../rootLifecycleFactory.test.ts | 20 +++++++------ .../rootLifecycle/rootLifecycleFactory.ts | 19 ++++++++---- .../src/database/connectors/sqlite3.ts | 10 +++---- packages/backend-plugin-api/api-report.md | 13 ++++++--- .../services/definitions/LifecycleService.ts | 14 ++++++--- .../src/services/definitions/index.ts | 1 + .../src/next/wiring/TestBackend.test.ts | 8 ++--- .../src/next/wiring/TestBackend.ts | 7 +---- .../src/service/CatalogPlugin.ts | 6 +--- 14 files changed, 88 insertions(+), 71 deletions(-) create mode 100644 .changeset/bright-plums-cough.md create mode 100644 .changeset/green-guests-flow.md diff --git a/.changeset/bright-plums-cough.md b/.changeset/bright-plums-cough.md new file mode 100644 index 0000000000..9af30f002e --- /dev/null +++ b/.changeset/bright-plums-cough.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +**BREAKING**: Split out the hook for both lifecycle services so that the first parameter of `addShutdownHook` is the hook function, and the second is the options. diff --git a/.changeset/green-guests-flow.md b/.changeset/green-guests-flow.md new file mode 100644 index 0000000000..688ff17dd9 --- /dev/null +++ b/.changeset/green-guests-flow.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-test-utils': patch +'@backstage/backend-app-api': patch +'@backstage/backend-common': patch +'@backstage/plugin-catalog-backend': patch +--- + +Updated usage of the lifecycle service. diff --git a/docs/backend-system/core-services/01-index.md b/docs/backend-system/core-services/01-index.md index b394391480..711357df61 100644 --- a/docs/backend-system/core-services/01-index.md +++ b/docs/backend-system/core-services/01-index.md @@ -479,10 +479,7 @@ createBackendPlugin({ // do some other stuff. }, 1000); - lifecycle.addShutdownHook({ - fn: () => clearInterval(interval), - logger, - }); + lifecycle.addShutdownHook(() => clearInterval(interval)); }, }); }, @@ -499,17 +496,19 @@ The following example shows how to override the default implementation of the li ```ts class MyCustomLifecycleService implements RootLifecycleService { - constructor(private readonly logger: LoggerService) { - ['SIGKILL', 'SIGTERM'].map(signal => - process.on(signal, () => this.shutdown()), - ); - } + constructor(private readonly logger: LoggerService) {} #isCalled = false; - #shutdownTasks: Array = []; + #shutdownTasks: Array<{ + hook: LifecycleServiceShutdownHook; + options?: LifecycleServiceShutdownOptions; + }> = []; - addShutdownHook(options: LifecycleServiceShutdownHook): void { - this.#shutdownTasks.push(options); + addShutdownHook( + hook: LifecycleServiceShutdownHook, + options?: LifecycleServiceShutdownOptions, + ): void { + this.#shutdownTasks.push({ hook, options }); } async shutdown(): Promise { @@ -520,10 +519,10 @@ class MyCustomLifecycleService implements RootLifecycleService { this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); await Promise.all( - this.#shutdownTasks.map(async hook => { - const { logger = this.logger } = hook; + this.#shutdownTasks.map(async ({ hook, options }) => { + const logger = options?.logger ?? this.logger; try { - await hook.fn(); + await hook(); logger.info(`Shutdown hook succeeded`); } catch (error) { logger.error(`Shutdown hook failed, ${error}`); diff --git a/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts b/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts index 299022079b..27c1a139fd 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycle/lifecycleFactory.ts @@ -17,6 +17,7 @@ import { createServiceFactory, coreServices, LifecycleServiceShutdownHook, + LifecycleServiceShutdownOptions, } from '@backstage/backend-plugin-api'; /** @@ -33,11 +34,12 @@ export const lifecycleFactory = createServiceFactory({ async factory({ rootLifecycle, logger, pluginMetadata }) { const plugin = pluginMetadata.getId(); return { - addShutdownHook(options: LifecycleServiceShutdownHook): void { - rootLifecycle.addShutdownHook({ - ...options, - - logger: options.logger?.child({ plugin }) ?? logger, + addShutdownHook( + hook: LifecycleServiceShutdownHook, + options?: LifecycleServiceShutdownOptions, + ): void { + rootLifecycle.addShutdownHook(hook, { + logger: options?.logger?.child({ plugin }) ?? logger, }); }, }; diff --git a/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterFactory.ts b/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterFactory.ts index 58f8cc2db1..f46f92741b 100644 --- a/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterFactory.ts +++ b/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterFactory.ts @@ -104,12 +104,7 @@ export const rootHttpRouterFactory = createServiceFactory({ { logger }, ); - lifecycle.addShutdownHook({ - async fn() { - await server.stop(); - }, - logger, - }); + lifecycle.addShutdownHook(() => server.stop()); await server.start(); diff --git a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.test.ts b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.test.ts index fc1c266775..3a93cacc8d 100644 --- a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.test.ts +++ b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.test.ts @@ -21,11 +21,7 @@ describe('lifecycleService', () => { it('should execute registered shutdown hook', async () => { const service = new BackendLifecycleImpl(getVoidLogger()); const hook = jest.fn(); - service.addShutdownHook({ - fn: async () => { - hook(); - }, - }); + service.addShutdownHook(() => hook()); // should not execute the hook more than once. await service.shutdown(); await service.shutdown(); @@ -35,10 +31,16 @@ describe('lifecycleService', () => { it('should not throw errors', async () => { const service = new BackendLifecycleImpl(getVoidLogger()); - service.addShutdownHook({ - fn: async () => { - throw new Error('oh no'); - }, + service.addShutdownHook(() => { + throw new Error('oh no'); + }); + await expect(service.shutdown()).resolves.toBeUndefined(); + }); + + it('should not throw async errors', async () => { + const service = new BackendLifecycleImpl(getVoidLogger()); + service.addShutdownHook(async () => { + throw new Error('oh no'); }); await expect(service.shutdown()).resolves.toBeUndefined(); }); diff --git a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts index ebff53e9a6..2b6f7eeafe 100644 --- a/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts +++ b/packages/backend-app-api/src/services/implementations/rootLifecycle/rootLifecycleFactory.ts @@ -18,6 +18,7 @@ import { createServiceFactory, coreServices, LifecycleServiceShutdownHook, + LifecycleServiceShutdownOptions, RootLifecycleService, LoggerService, } from '@backstage/backend-plugin-api'; @@ -26,10 +27,16 @@ export class BackendLifecycleImpl implements RootLifecycleService { constructor(private readonly logger: LoggerService) {} #isCalled = false; - #shutdownTasks: Array = []; + #shutdownTasks: Array<{ + hook: LifecycleServiceShutdownHook; + options?: LifecycleServiceShutdownOptions; + }> = []; - addShutdownHook(options: LifecycleServiceShutdownHook): void { - this.#shutdownTasks.push(options); + addShutdownHook( + hook: LifecycleServiceShutdownHook, + options?: LifecycleServiceShutdownOptions, + ): void { + this.#shutdownTasks.push({ hook, options }); } async shutdown(): Promise { @@ -40,10 +47,10 @@ export class BackendLifecycleImpl implements RootLifecycleService { this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); await Promise.all( - this.#shutdownTasks.map(async hook => { - const { logger = this.logger } = hook; + this.#shutdownTasks.map(async ({ hook, options }) => { + const logger = options?.logger ?? this.logger; try { - await hook.fn(); + await hook(); logger.info(`Shutdown hook succeeded`); } catch (error) { logger.error(`Shutdown hook failed, ${error}`); diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index e1ed6b18e9..3cb0f57de6 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -81,12 +81,10 @@ export function createSqliteDatabaseClient( }); // If the dev store is available we save the database state on shutdown - deps.lifecycle.addShutdownHook({ - async fn() { - const connection = await database.client.acquireConnection(); - const data = connection.serialize(); - await devStore.save(dataKey, data); - }, + deps.lifecycle.addShutdownHook(async () => { + const connection = await database.client.acquireConnection(); + const data = connection.serialize(); + await devStore.save(dataKey, data); }); } else { database = knexFactory(knexConfig); diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index a7700d81ac..1add61348c 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -294,14 +294,19 @@ export interface IdentityService extends IdentityApi {} // @public (undocumented) export interface LifecycleService { - addShutdownHook(options: LifecycleServiceShutdownHook): void; + addShutdownHook( + hook: LifecycleServiceShutdownHook, + options?: LifecycleServiceShutdownOptions, + ): void; } // @public (undocumented) -export type LifecycleServiceShutdownHook = { - fn: () => void | Promise; +export type LifecycleServiceShutdownHook = () => void | Promise; + +// @public (undocumented) +export interface LifecycleServiceShutdownOptions { logger?: LoggerService; -}; +} // @public export interface LoggerService { diff --git a/packages/backend-plugin-api/src/services/definitions/LifecycleService.ts b/packages/backend-plugin-api/src/services/definitions/LifecycleService.ts index 59d14aa61d..89351194e6 100644 --- a/packages/backend-plugin-api/src/services/definitions/LifecycleService.ts +++ b/packages/backend-plugin-api/src/services/definitions/LifecycleService.ts @@ -19,14 +19,17 @@ import { LoggerService } from './LoggerService'; /** * @public */ -export type LifecycleServiceShutdownHook = { - fn: () => void | Promise; +export type LifecycleServiceShutdownHook = () => void | Promise; +/** + * @public + */ +export interface LifecycleServiceShutdownOptions { /** * Optional {@link LoggerService} that will be used for logging instead of the default logger. */ logger?: LoggerService; -}; +} /** * @public @@ -35,5 +38,8 @@ export interface LifecycleService { /** * Register a function to be called when the backend is shutting down. */ - addShutdownHook(options: LifecycleServiceShutdownHook): void; + addShutdownHook( + hook: LifecycleServiceShutdownHook, + options?: LifecycleServiceShutdownOptions, + ): void; } diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index c5af0967bd..720b4d634d 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -28,6 +28,7 @@ export type { HttpRouterService } from './HttpRouterService'; export type { LifecycleService, LifecycleServiceShutdownHook, + LifecycleServiceShutdownOptions, } from './LifecycleService'; export type { LoggerService, LogMeta } from './LoggerService'; export type { PermissionsService } from './PermissionsService'; diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index dca463a9ae..9bda3f3f5b 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -40,10 +40,8 @@ beforeAll(async () => { env.registerInit({ deps: { lifecycle: coreServices.lifecycle }, async init({ lifecycle }) { - lifecycle.addShutdownHook({ - fn() { - globalTestBackendHasBeenStopped = true; - }, + lifecycle.addShutdownHook(() => { + globalTestBackendHasBeenStopped = true; }); }, }); @@ -145,7 +143,7 @@ describe('TestBackend', () => { lifecycle: coreServices.lifecycle, }, async init({ lifecycle }) { - lifecycle.addShutdownHook({ fn: shutdownSpy }); + lifecycle.addShutdownHook(shutdownSpy); }, }); }, diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.ts index db1016fcb6..27a8c5bceb 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.ts @@ -129,12 +129,7 @@ export async function startTestBackend< { logger }, ); - lifecycle.addShutdownHook({ - async fn() { - await server.stop(); - }, - logger, - }); + lifecycle.addShutdownHook(() => server.stop(), { logger }); await server.start(); diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index effed7f8f1..a726d3261b 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -97,11 +97,7 @@ export const catalogPlugin = createBackendPlugin({ const { processingEngine, router } = await builder.build(); await processingEngine.start(); - lifecycle.addShutdownHook({ - fn: async () => { - await processingEngine.stop(); - }, - }); + lifecycle.addShutdownHook(() => processingEngine.stop()); httpRouter.use(router); }, }); From 000fd3f4a8a57311c5301d0de0fd6621f50fe607 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 17:24:10 +0100 Subject: [PATCH 114/130] eslint-plugin: fix no-undeclared-imports test on windows Signed-off-by: Patrik Oldsberg --- .../src/no-undeclared-imports.test.ts | 142 ++++++++++++------ 1 file changed, 94 insertions(+), 48 deletions(-) diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts index c156558e8d..1b5cbee853 100644 --- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -15,11 +15,11 @@ */ import { RuleTester } from 'eslint'; -import path from 'path'; +import { join as joinPath } from 'path'; import rule from '../rules/no-undeclared-imports'; const RULE = 'no-undeclared-imports'; -const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); +const FIXTURE = joinPath(__dirname, '__fixtures__/monorepo'); const ERR_UNDECLARED = ( name: string, @@ -27,7 +27,10 @@ const ERR_UNDECLARED = ( path: string, flag?: string, ) => ({ - message: `${name} must be declared in ${field} of ${path}/package.json, run 'yarn --cwd ${path} add${ + message: `${name} must be declared in ${field} of ${joinPath( + path, + 'package.json', + )}, run 'yarn --cwd ${path} add${ flag ? ` ${flag}` : '' } ${name}' from the project root.`, }); @@ -37,7 +40,10 @@ const ERR_SWITCHED = ( field: string, path: string, ) => ({ - message: `${name} is declared in ${old}, but should be moved to ${field} in ${path}/package.json.`, + message: `${name} is declared in ${old}, but should be moved to ${field} in ${joinPath( + path, + 'package.json', + )}.`, }); process.chdir(FIXTURE); @@ -53,141 +59,181 @@ ruleTester.run(RULE, rule, { valid: [ { code: `import '@internal/foo'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), }, { code: `import '@internal/bar'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), }, { code: `import 'react'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), }, { code: `import '@internal/foo'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), }, { code: `import '@internal/bar'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), }, { code: `import 'lodash'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), }, { code: `import 'react'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), }, { // We're only able to validate literals code: `require('lod' + 'ash')`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), }, ], invalid: [ { code: `import 'lodash'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), errors: [ ERR_SWITCHED( 'lodash', 'devDependencies', 'dependencies', - 'packages/foo', + joinPath('packages', 'foo'), ), ], }, { code: `import 'react-router'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), errors: [ ERR_SWITCHED( 'react-router', 'dependencies', 'peerDependencies', - 'packages/bar', + joinPath('packages', 'bar'), ), ], }, { code: `import 'react-router-dom'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), errors: [ ERR_SWITCHED( 'react-router-dom', 'devDependencies', 'peerDependencies', - 'packages/bar', + joinPath('packages', 'bar'), ), ], }, { code: `import 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], }, { code: `import { debounce } from 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], }, { code: `import * as _ from 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], }, { code: `import _ from 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], }, { code: `import('lodash')`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], }, { code: `require('lodash')`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], - }, - { - code: `import 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), - errors: [ERR_UNDECLARED('lodash', 'dependencies', 'packages/bar')], - }, - { - code: `import 'lodash'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), errors: [ - ERR_UNDECLARED('lodash', 'devDependencies', 'packages/bar', '--dev'), + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], + }, + { + code: `import 'lodash'`, + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR_UNDECLARED('lodash', 'dependencies', joinPath('packages', 'bar')), + ], + }, + { + code: `import 'lodash'`, + filename: joinPath(FIXTURE, 'packages/bar/src/index.test.ts'), + errors: [ + ERR_UNDECLARED( + 'lodash', + 'devDependencies', + joinPath('packages', 'bar'), + '--dev', + ), ], }, { code: `import 'react'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), errors: [ - ERR_UNDECLARED('react', 'peerDependencies', 'packages/bar', '--peer'), + ERR_UNDECLARED( + 'react', + 'peerDependencies', + joinPath('packages', 'bar'), + '--peer', + ), ], }, { code: `import 'react'`, - filename: path.join(FIXTURE, 'packages/bar/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/bar/src/index.test.ts'), errors: [ - ERR_UNDECLARED('react', 'peerDependencies', 'packages/bar', '--peer'), + ERR_UNDECLARED( + 'react', + 'peerDependencies', + joinPath('packages', 'bar'), + '--peer', + ), ], }, { code: `import 'react-dom'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.ts'), - errors: [ERR_UNDECLARED('react-dom', 'dependencies', 'packages/foo')], + filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), + errors: [ + ERR_UNDECLARED( + 'react-dom', + 'dependencies', + joinPath('packages', 'foo'), + ), + ], }, { code: `import 'react-dom'`, - filename: path.join(FIXTURE, 'packages/foo/src/index.test.ts'), + filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), errors: [ - ERR_UNDECLARED('react-dom', 'devDependencies', 'packages/foo', '--dev'), + ERR_UNDECLARED( + 'react-dom', + 'devDependencies', + joinPath('packages', 'foo'), + '--dev', + ), ], }, ], From f1adb2e36b460b5c996504d1a6ed897d04968c4d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 17:34:26 +0100 Subject: [PATCH 115/130] backend-test-utils: remove mock service refs Signed-off-by: Patrik Oldsberg --- .changeset/thin-donkeys-attack.md | 5 ++++ packages/backend-test-utils/api-report.md | 27 ------------------- .../src/next/services/mockServices.ts | 24 +++++------------ 3 files changed, 12 insertions(+), 44 deletions(-) create mode 100644 .changeset/thin-donkeys-attack.md diff --git a/.changeset/thin-donkeys-attack.md b/.changeset/thin-donkeys-attack.md new file mode 100644 index 0000000000..2fd0aebb6a --- /dev/null +++ b/.changeset/thin-donkeys-attack.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Removed the `ref` from all `mockServices`. diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 232f7e8136..6e0edde88b 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -20,7 +20,6 @@ import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { PermissionsService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; -import { RootLoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -34,8 +33,6 @@ export function isDockerDisabledForTests(): boolean; export namespace mockServices { // (undocumented) export namespace cache { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } @@ -47,22 +44,16 @@ export namespace mockServices { export type Options = { data?: JsonObject; }; - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: (options?: Options | undefined) => ServiceFactory; } // (undocumented) export namespace database { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace httpRouter { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: ( options?: HttpRouterFactoryOptions | undefined, @@ -72,36 +63,26 @@ export namespace mockServices { export function identity(): IdentityService; // (undocumented) export namespace identity { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace lifecycle { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace logger { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace permissions { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace rootLifecycle { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } @@ -120,15 +101,11 @@ export namespace mockServices { debug: boolean; }; }; - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: (options?: Options | undefined) => ServiceFactory; } // (undocumented) export namespace scheduler { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } @@ -136,15 +113,11 @@ export namespace mockServices { export function tokenManager(): TokenManagerService; // (undocumented) export namespace tokenManager { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } // (undocumented) export namespace urlReader { - const // (undocumented) - ref: ServiceRef; const // (undocumented) factory: () => ServiceFactory; } diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index f86d8bac55..ef32993611 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -62,8 +62,7 @@ export namespace mockServices { export namespace config { export type Options = { data?: JsonObject }; - export const ref = coreServices.config; - export const factory = simpleFactory(ref, config); + export const factory = simpleFactory(coreServices.config, config); } export function rootLogger(options?: rootLogger.Options): LoggerService { @@ -76,8 +75,7 @@ export namespace mockServices { | { error: boolean; warn: boolean; info: boolean; debug: boolean }; }; - export const ref = coreServices.rootLogger; - export const factory = simpleFactory(ref, rootLogger); + export const factory = simpleFactory(coreServices.rootLogger, rootLogger); } export function tokenManager(): TokenManagerService { @@ -93,55 +91,47 @@ export namespace mockServices { }; } export namespace tokenManager { - export const ref = coreServices.tokenManager; - export const factory = simpleFactory(ref, tokenManager); + export const factory = simpleFactory( + coreServices.tokenManager, + tokenManager, + ); } export function identity(): IdentityService { return new MockIdentityService(); } export namespace identity { - export const ref = coreServices.identity; - export const factory = simpleFactory(ref, identity); + export const factory = simpleFactory(coreServices.identity, identity); } // TODO(Rugvip): Not all core services have implementations available here yet. // some may need a bit more refactoring for it to be simpler to // re-implement functioning mock versions here. export namespace cache { - export const ref = coreServices.cache; export const factory = cacheFactory; } export namespace database { - export const ref = coreServices.database; export const factory = databaseFactory; } export namespace httpRouter { - export const ref = coreServices.httpRouter; export const factory = httpRouterFactory; } export namespace lifecycle { - export const ref = coreServices.lifecycle; export const factory = lifecycleFactory; } export namespace logger { - export const ref = coreServices.logger; export const factory = loggerFactory; } export namespace permissions { - export const ref = coreServices.permissions; export const factory = permissionsFactory; } export namespace rootLifecycle { - export const ref = coreServices.rootLifecycle; export const factory = rootLifecycleFactory; } export namespace scheduler { - export const ref = coreServices.scheduler; export const factory = schedulerFactory; } export namespace urlReader { - export const ref = coreServices.urlReader; export const factory = urlReaderFactory; } } From 1835311713c3ee767376250f4e5252d06a554c67 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 17:36:22 +0100 Subject: [PATCH 116/130] backend-test-utils: explicit return type for mockServices.config() Signed-off-by: Patrik Oldsberg --- .changeset/four-tables-fix.md | 5 +++++ packages/backend-test-utils/api-report.md | 3 +-- .../backend-test-utils/src/next/services/mockServices.ts | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/four-tables-fix.md diff --git a/.changeset/four-tables-fix.md b/.changeset/four-tables-fix.md new file mode 100644 index 0000000000..f17f57e253 --- /dev/null +++ b/.changeset/four-tables-fix.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Added explicit return type signature for `mockServices.config()`. diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 6e0edde88b..672c574cb2 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -6,7 +6,6 @@ import { Backend } from '@backstage/backend-app-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheService } from '@backstage/backend-plugin-api'; -import { ConfigReader } from '@backstage/config'; import { ConfigService } from '@backstage/backend-plugin-api'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { ExtendedHttpServer } from '@backstage/backend-app-api'; @@ -37,7 +36,7 @@ export namespace mockServices { factory: () => ServiceFactory; } // (undocumented) - export function config(options?: config.Options): ConfigReader; + export function config(options?: config.Options): ConfigService; // (undocumented) export namespace config { // (undocumented) diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index ef32993611..5f771f13c5 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -15,6 +15,7 @@ */ import { + ConfigService, coreServices, createServiceFactory, IdentityService, @@ -56,7 +57,7 @@ function simpleFactory( * @alpha */ export namespace mockServices { - export function config(options?: config.Options) { + export function config(options?: config.Options): ConfigService { return new ConfigReader(options?.data, 'mock-config'); } export namespace config { From 1d4692fb3ddfcfdd31181d41e6e3def90b06f052 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:42:10 +0000 Subject: [PATCH 117/130] fix(deps): update dependency @esbuild-kit/cjs-loader to v2.4.2 Signed-off-by: Renovate Bot --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 83503fb359..ecb0826a83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9212,12 +9212,12 @@ __metadata: linkType: hard "@esbuild-kit/cjs-loader@npm:^2.4.1": - version: 2.4.1 - resolution: "@esbuild-kit/cjs-loader@npm:2.4.1" + version: 2.4.2 + resolution: "@esbuild-kit/cjs-loader@npm:2.4.2" dependencies: "@esbuild-kit/core-utils": ^3.0.0 - get-tsconfig: ^4.2.0 - checksum: a516065907be0ead76ac2199ccb08ff92659ba5e2edb4bb8772b6a63afe4faed7eb45a3b4d87266a68c7c135c3dba971cd087bc6f16c382356e835c7dd3440f5 + get-tsconfig: ^4.4.0 + checksum: e346e339bfc7eff5c52c270fd0ec06a7f2341b624adfb69f84b7d83f119c35070420906f2761a0b4604e0a0ec90e35eaf12544585476c428ed6d6ee3b250c0fe languageName: node linkType: hard @@ -23525,10 +23525,10 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.2.0": - version: 4.3.0 - resolution: "get-tsconfig@npm:4.3.0" - checksum: 2597aab99aa3a24db209e192a3e5874ac47fc5abc71703ee26346e0c5816cb346ca09fc813c739db5862d3a2905d89aeca1b0cbc46c2b272398d672309aaf414 +"get-tsconfig@npm:^4.4.0": + version: 4.4.0 + resolution: "get-tsconfig@npm:4.4.0" + checksum: e193558b4f0c84c81ae9688cf5b9950dc0b341e44f91b002713fd0c37cfb73108e1cd9998ed540bcc423f193fde32cc58a15e99dd469f5158a2eb4a148611176 languageName: node linkType: hard From a1e4a3e842f76e37e19ad7a76c44f8594e88975f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:42:41 +0000 Subject: [PATCH 118/130] fix(deps): update dependency @google-cloud/storage to v6.9.2 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 83503fb359..c3c0faab14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9602,8 +9602,8 @@ __metadata: linkType: hard "@google-cloud/storage@npm:^6.0.0": - version: 6.9.1 - resolution: "@google-cloud/storage@npm:6.9.1" + version: 6.9.2 + resolution: "@google-cloud/storage@npm:6.9.2" dependencies: "@google-cloud/paginator": ^3.0.7 "@google-cloud/projectify": ^3.0.0 @@ -9622,7 +9622,7 @@ __metadata: retry-request: ^5.0.0 teeny-request: ^8.0.0 uuid: ^8.0.0 - checksum: 9967bc0e80d1531f91030d27fc46cef0242f70dde2064b051104d8d261f1d3cba38b51789b0ae0850bf99b13d093365f85c85c43a2d9468d81f0f98238f76e78 + checksum: e4962be29ca3dd722019db95f99b095993534915d663a82b41c95beaf284d878de5b1357102441e4e375de838299f6d82eff55a2c4e9f610f9a12feac21e2735 languageName: node linkType: hard From 0ff03319be02103dea4bcb36ffcfeba272c31af6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 17:49:57 +0100 Subject: [PATCH 119/130] backend-plugin-api: switch createBackendPlugin id -> pluginId Signed-off-by: Patrik Oldsberg --- .changeset/bright-teachers-compete.md | 5 ++++ .changeset/little-jars-lay.md | 18 ++++++++++++++ .../backend-system/architecture/04-plugins.md | 4 ++-- .../architecture/05-extension-points.md | 2 +- .../architecture/07-naming-patterns.md | 4 ++-- .../building-plugins-and-modules/01-index.md | 6 ++--- .../08-migrating.md | 6 ++--- docs/backend-system/core-services/01-index.md | 24 +++++++++---------- .../02-adding-a-basic-permission-check.md | 2 +- docs/plugins/new-backend-system.md | 4 ++-- .../scheduler/schedulerFactory.test.ts | 2 +- packages/backend-common/src/legacy.ts | 2 +- .../src/CreateBackend.test.ts | 2 +- packages/backend-plugin-api/api-report.md | 2 +- .../src/wiring/factories.test.ts | 10 ++++---- .../src/wiring/factories.ts | 16 +++++++++---- .../src/next/wiring/TestBackend.test.ts | 4 ++-- plugins/app-backend/src/service/appPlugin.ts | 2 +- plugins/bazaar-backend/src/plugin.ts | 2 +- .../src/service/CatalogPlugin.ts | 2 +- .../src/service/EventsPlugin.ts | 2 +- .../example-todo-list-backend/src/plugin.ts | 2 +- plugins/kafka-backend/src/plugin.ts | 2 +- plugins/periskop-backend/src/plugin.ts | 2 +- plugins/proxy-backend/src/plugin.ts | 2 +- .../src/ScaffolderPlugin.ts | 2 +- plugins/user-settings-backend/src/plugin.ts | 2 +- 27 files changed, 82 insertions(+), 51 deletions(-) create mode 100644 .changeset/bright-teachers-compete.md create mode 100644 .changeset/little-jars-lay.md diff --git a/.changeset/bright-teachers-compete.md b/.changeset/bright-teachers-compete.md new file mode 100644 index 0000000000..3cc900d613 --- /dev/null +++ b/.changeset/bright-teachers-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +**BREAKING**: The plugin ID option passed to `createBackendPlugin` is now `pluginId`, rather than just `id`. This is to make it match `createBackendModule` more closely. diff --git a/.changeset/little-jars-lay.md b/.changeset/little-jars-lay.md new file mode 100644 index 0000000000..161dc099e9 --- /dev/null +++ b/.changeset/little-jars-lay.md @@ -0,0 +1,18 @@ +--- +'@internal/plugin-todo-list-backend': patch +'@backstage/plugin-user-settings-backend': patch +'@backstage/backend-test-utils': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/backend-defaults': patch +'@backstage/backend-app-api': patch +'@backstage/plugin-periskop-backend': patch +'@backstage/backend-common': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-bazaar-backend': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-kafka-backend': patch +'@backstage/plugin-proxy-backend': patch +'@backstage/plugin-app-backend': patch +--- + +Updated usage of `createBackendPlugin`. diff --git a/docs/backend-system/architecture/04-plugins.md b/docs/backend-system/architecture/04-plugins.md index 7721b1c9f7..e73f086c43 100644 --- a/docs/backend-system/architecture/04-plugins.md +++ b/docs/backend-system/architecture/04-plugins.md @@ -22,7 +22,7 @@ import { } from '@backstage/backend-plugin-api'; export const examplePlugin = createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -53,7 +53,7 @@ export interface ExamplePluginOptions { export const examplePlugin = createBackendPlugin( (options?: ExamplePluginOptions) => ({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { diff --git a/docs/backend-system/architecture/05-extension-points.md b/docs/backend-system/architecture/05-extension-points.md index d16a773c0c..278c565472 100644 --- a/docs/backend-system/architecture/05-extension-points.md +++ b/docs/backend-system/architecture/05-extension-points.md @@ -44,7 +44,7 @@ class ActionsExtension implements ScaffolderActionsExtensionPoint { export const scaffolderPlugin = createBackendPlugin( { - id: 'scaffolder', + pluginId: 'scaffolder', register(env) { const actionsExtensions = new ActionsExtension(); env.registerExtensionPoint( diff --git a/docs/backend-system/architecture/07-naming-patterns.md b/docs/backend-system/architecture/07-naming-patterns.md index dc781a61ac..b4b4c6c70f 100644 --- a/docs/backend-system/architecture/07-naming-patterns.md +++ b/docs/backend-system/architecture/07-naming-patterns.md @@ -21,7 +21,7 @@ Example: ```ts export const catalogPlugin = createBackendPlugin({ - id: 'catalog', + pluginId: 'catalog', ... }) ``` @@ -36,7 +36,7 @@ export const catalogPlugin = createBackendPlugin({ Example: ```ts -export const catalogModuleGithubEntityProvider = createBackendPlugin({ +export const catalogModuleGithubEntityProvider = createBackendModule({ pluginId: 'catalog', moduleId: 'githubEntityProvider', ... diff --git a/docs/backend-system/building-plugins-and-modules/01-index.md b/docs/backend-system/building-plugins-and-modules/01-index.md index 5fefb3ff16..cae5052d04 100644 --- a/docs/backend-system/building-plugins-and-modules/01-index.md +++ b/docs/backend-system/building-plugins-and-modules/01-index.md @@ -36,7 +36,7 @@ import { import { createExampleRouter } from './router'; export const examplePlugin = createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -183,7 +183,7 @@ that needs to be different across environments. import { coreServices } from '@backstage/backend-plugin-api'; export const examplePlugin = createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { config: coreServices.config }, @@ -216,7 +216,7 @@ export interface ExampleOptions { export const examplePlugin = createBackendPlugin( (options?: ExampleOptions) => ({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { diff --git a/docs/backend-system/building-plugins-and-modules/08-migrating.md b/docs/backend-system/building-plugins-and-modules/08-migrating.md index ec5836a126..687f4c1d5e 100644 --- a/docs/backend-system/building-plugins-and-modules/08-migrating.md +++ b/docs/backend-system/building-plugins-and-modules/08-migrating.md @@ -46,7 +46,7 @@ import { Router } from 'express'; import { KubernetesBuilder } from './KubernetesBuilder'; export const kubernetesPlugin = createBackendPlugin({ - id: 'kubernetes', + pluginId: 'kubernetes', register(env) { env.registerInit({ deps: { @@ -95,7 +95,7 @@ export interface KubernetesOptions { } const kubernetesPlugin = createBackendPlugin((options: KubernetesOptions) => ({ - id: 'kubernetes', + pluginId: 'kubernetes', register(env) { env.registerInit({ deps: { @@ -182,7 +182,7 @@ class ClusterSupplier implements KubernetesClusterSupplierExtensionPoint { } export const kubernetesPlugin = createBackendPlugin({ - id: 'kubernetes', + pluginId: 'kubernetes', register(env) { const extensionPoint = new ClusterSupplier(); // We register the extension point with the backend, which allows modules to diff --git a/docs/backend-system/core-services/01-index.md b/docs/backend-system/core-services/01-index.md index b394391480..18c7b1079f 100644 --- a/docs/backend-system/core-services/01-index.md +++ b/docs/backend-system/core-services/01-index.md @@ -33,7 +33,7 @@ import { import { Router } from 'express'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { http: coreServices.httpRouter }, @@ -86,7 +86,7 @@ import { import { Router } from 'express'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -157,7 +157,7 @@ import { } from '@backstage/backend-plugin-api'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -215,7 +215,7 @@ import { } from '@backstage/backend-plugin-api'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -288,7 +288,7 @@ import { } from '@backstage/backend-plugin-api'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -327,7 +327,7 @@ import { import { resolvePackagePath } from '@backstage/backend-common'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -366,7 +366,7 @@ import { import { fetch } from 'node-fetch'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -397,7 +397,7 @@ import { import { Router } from 'express'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -465,7 +465,7 @@ import { } from '@backstage/backend-plugin-api'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -564,7 +564,7 @@ import { import { Router } from 'express'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -613,7 +613,7 @@ import { import { fetch } from 'node-fetch'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { @@ -652,7 +652,7 @@ import { import os from 'os'; createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env) { env.registerInit({ deps: { diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 551e48f91a..2ceb8057f4 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -301,7 +301,7 @@ Finally, we need to update `plugins/todo-list-backend/src/plugin.ts`: * @alpha */ export const exampleTodoListPlugin = createBackendPlugin({ - id: 'exampleTodoList', + pluginId: 'exampleTodoList', register(env) { env.registerInit({ deps: { diff --git a/docs/plugins/new-backend-system.md b/docs/plugins/new-backend-system.md index bfc6f79661..785d364ae2 100644 --- a/docs/plugins/new-backend-system.md +++ b/docs/plugins/new-backend-system.md @@ -84,7 +84,7 @@ import { // export type ExamplePluginOptions = { exampleOption: boolean }; export const examplePlugin = createBackendPlugin({ // unique id for the plugin - id: 'example', + pluginId: 'example', // It's possible to provide options to the plugin // register(env, options: ExamplePluginOptions) { register(env) { @@ -111,7 +111,7 @@ If we wanted our plugin to accept options as well, we'd accept the options as th ```ts export const examplePlugin = createBackendPlugin({ - id: 'example', + pluginId: 'example', register(env, options?: { silent?: boolean }) { env.registerInit({ deps: { logger: coreServices.logger }, diff --git a/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.test.ts b/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.test.ts index 1aaf4ec71d..15ec364ac5 100644 --- a/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.test.ts +++ b/packages/backend-app-api/src/services/implementations/scheduler/schedulerFactory.test.ts @@ -28,7 +28,7 @@ describe('schedulerFactory', () => { const subject = schedulerFactory(); const plugin = createBackendPlugin({ - id: 'example', + pluginId: 'example', register(reg) { reg.registerInit({ deps: { diff --git a/packages/backend-common/src/legacy.ts b/packages/backend-common/src/legacy.ts index 30e7dd9dc5..214e65e0c7 100644 --- a/packages/backend-common/src/legacy.ts +++ b/packages/backend-common/src/legacy.ts @@ -60,7 +60,7 @@ export function makeLegacyPlugin< }>, ) => { const compatPlugin = createBackendPlugin({ - id: name, + pluginId: name, register(env) { env.registerInit({ deps: { ...envMapping, _router: coreServices.httpRouter }, diff --git a/packages/backend-defaults/src/CreateBackend.test.ts b/packages/backend-defaults/src/CreateBackend.test.ts index 2cfb92c3a6..8d56cd0986 100644 --- a/packages/backend-defaults/src/CreateBackend.test.ts +++ b/packages/backend-defaults/src/CreateBackend.test.ts @@ -143,7 +143,7 @@ describe('createBackend', () => { expect.assertions(3); backend.add( createBackendPlugin({ - id: 'test', + pluginId: 'test', register(reg) { reg.registerInit({ deps: { diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index a7700d81ac..9a6f05babe 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -47,7 +47,7 @@ export interface BackendModuleRegistrationPoints { // @public export interface BackendPluginConfig { - id: string; + pluginId: string; // (undocumented) register(reg: BackendPluginRegistrationPoints): void; } diff --git a/packages/backend-plugin-api/src/wiring/factories.test.ts b/packages/backend-plugin-api/src/wiring/factories.test.ts index dbdf0ead14..8e1b30ea53 100644 --- a/packages/backend-plugin-api/src/wiring/factories.test.ts +++ b/packages/backend-plugin-api/src/wiring/factories.test.ts @@ -33,7 +33,7 @@ describe('createExtensionPoint', () => { describe('createBackendPlugin', () => { it('should create a BackendPlugin', () => { const plugin = createBackendPlugin((_options: { a: string }) => ({ - id: 'x', + pluginId: 'x', register() {}, })); expect(plugin).toBeDefined(); @@ -47,7 +47,7 @@ describe('createBackendPlugin', () => { it('should create plugins with optional options', () => { const plugin = createBackendPlugin((_options?: { a: string }) => ({ - id: 'x', + pluginId: 'x', register() {}, })); expect(plugin).toBeDefined(); @@ -59,7 +59,7 @@ describe('createBackendPlugin', () => { it('should create plugins without options', () => { const plugin = createBackendPlugin({ - id: 'x', + pluginId: 'x', register() {}, }); expect(plugin).toBeDefined(); @@ -74,7 +74,7 @@ describe('createBackendPlugin', () => { a: string; } const plugin = createBackendPlugin((_options: TestOptions) => ({ - id: 'x', + pluginId: 'x', register() {}, })); expect(plugin).toBeDefined(); @@ -91,7 +91,7 @@ describe('createBackendPlugin', () => { a: string; } const plugin = createBackendPlugin((_options?: TestOptions) => ({ - id: 'x', + pluginId: 'x', register() {}, })); expect(plugin).toBeDefined(); diff --git a/packages/backend-plugin-api/src/wiring/factories.ts b/packages/backend-plugin-api/src/wiring/factories.ts index d281335a8e..6f37efabfe 100644 --- a/packages/backend-plugin-api/src/wiring/factories.ts +++ b/packages/backend-plugin-api/src/wiring/factories.ts @@ -72,7 +72,7 @@ export interface BackendPluginConfig { * * @see {@link https://backstage.io/docs/backend-system/architecture/naming-patterns | Recommended naming patterns} */ - id: string; + pluginId: string; register(reg: BackendPluginRegistrationPoints): void; } @@ -87,10 +87,18 @@ export function createBackendPlugin( config: BackendPluginConfig | ((...params: TOptions) => BackendPluginConfig), ): (...params: TOptions) => BackendFeature { if (typeof config === 'function') { - return config; + return (...options: TOptions) => { + const c = config(...options); + return { + ...c, + id: c.pluginId, + }; + }; } - - return () => config; + return () => ({ + ...config, + id: config.pluginId, + }); } /** diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index dca463a9ae..eca003c70b 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -165,7 +165,7 @@ describe('TestBackend', () => { expect.assertions(2); const testPlugin = createBackendPlugin({ - id: 'test', + pluginId: 'test', register(env) { env.registerInit({ deps: { @@ -201,7 +201,7 @@ describe('TestBackend', () => { it('should allow making requests via supertest', async () => { const testPlugin = createBackendPlugin({ - id: 'test', + pluginId: 'test', register(env) { env.registerInit({ deps: { diff --git a/plugins/app-backend/src/service/appPlugin.ts b/plugins/app-backend/src/service/appPlugin.ts index b12a8d6877..a29f8fd5a8 100644 --- a/plugins/app-backend/src/service/appPlugin.ts +++ b/plugins/app-backend/src/service/appPlugin.ts @@ -71,7 +71,7 @@ export type AppPluginOptions = { * @alpha */ export const appPlugin = createBackendPlugin((options: AppPluginOptions) => ({ - id: 'app', + pluginId: 'app', register(env) { env.registerInit({ deps: { diff --git a/plugins/bazaar-backend/src/plugin.ts b/plugins/bazaar-backend/src/plugin.ts index 03491950e8..62622717c8 100644 --- a/plugins/bazaar-backend/src/plugin.ts +++ b/plugins/bazaar-backend/src/plugin.ts @@ -27,7 +27,7 @@ import { createRouter } from './service/router'; * @alpha */ export const bazaarPlugin = createBackendPlugin({ - id: 'bazaar', + pluginId: 'bazaar', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index effed7f8f1..aadaf99a10 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -56,7 +56,7 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { * @alpha */ export const catalogPlugin = createBackendPlugin({ - id: 'catalog', + pluginId: 'catalog', register(env) { const processingExtensions = new CatalogExtensionPointImpl(); // plugins depending on this API will be initialized before this plugins init method is executed. diff --git a/plugins/events-backend/src/service/EventsPlugin.ts b/plugins/events-backend/src/service/EventsPlugin.ts index 29456d71f7..402104b56c 100644 --- a/plugins/events-backend/src/service/EventsPlugin.ts +++ b/plugins/events-backend/src/service/EventsPlugin.ts @@ -80,7 +80,7 @@ class EventsExtensionPointImpl implements EventsExtensionPoint { * @alpha */ export const eventsPlugin = createBackendPlugin({ - id: 'events', + pluginId: 'events', register(env) { const extensionPoint = new EventsExtensionPointImpl(); env.registerExtensionPoint(eventsExtensionPoint, extensionPoint); diff --git a/plugins/example-todo-list-backend/src/plugin.ts b/plugins/example-todo-list-backend/src/plugin.ts index ba25ed7218..42985f6e88 100644 --- a/plugins/example-todo-list-backend/src/plugin.ts +++ b/plugins/example-todo-list-backend/src/plugin.ts @@ -27,7 +27,7 @@ import { createRouter } from './service/router'; * @alpha */ export const exampleTodoListPlugin = createBackendPlugin({ - id: 'exampleTodoList', + pluginId: 'exampleTodoList', register(env) { env.registerInit({ deps: { diff --git a/plugins/kafka-backend/src/plugin.ts b/plugins/kafka-backend/src/plugin.ts index f627e5a0bd..2442eddbaa 100644 --- a/plugins/kafka-backend/src/plugin.ts +++ b/plugins/kafka-backend/src/plugin.ts @@ -27,7 +27,7 @@ import { createRouter } from './service/router'; * @alpha */ export const kafkaPlugin = createBackendPlugin({ - id: 'kafka', + pluginId: 'kafka', register(env) { env.registerInit({ deps: { diff --git a/plugins/periskop-backend/src/plugin.ts b/plugins/periskop-backend/src/plugin.ts index b5dcfb1425..933c864d50 100644 --- a/plugins/periskop-backend/src/plugin.ts +++ b/plugins/periskop-backend/src/plugin.ts @@ -27,7 +27,7 @@ import { createRouter } from './service/router'; * @alpha */ export const periskopPlugin = createBackendPlugin({ - id: 'periskop', + pluginId: 'periskop', register(env) { env.registerInit({ deps: { diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/plugin.ts index e9d6d515c1..d095bee527 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/plugin.ts @@ -28,7 +28,7 @@ import { createRouter } from './service/router'; */ export const proxyPlugin = createBackendPlugin( (options?: { skipInvalidProxies?: boolean }) => ({ - id: 'proxy', + pluginId: 'proxy', register(env) { env.registerInit({ deps: { diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 43b8d30f99..3b0d26be21 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -64,7 +64,7 @@ class ScaffolderActionsExtensionPointImpl */ export const scaffolderPlugin = createBackendPlugin( (options: ScaffolderPluginOptions) => ({ - id: 'scaffolder', + pluginId: 'scaffolder', register(env) { const actionsExtensions = new ScaffolderActionsExtensionPointImpl(); diff --git a/plugins/user-settings-backend/src/plugin.ts b/plugins/user-settings-backend/src/plugin.ts index 83120cc180..e200d22ffe 100644 --- a/plugins/user-settings-backend/src/plugin.ts +++ b/plugins/user-settings-backend/src/plugin.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * @alpha */ export const userSettingsPlugin = createBackendPlugin({ - id: 'userSettings', + pluginId: 'userSettings', register(env) { env.registerInit({ deps: { From 181c03edb528d094ff15d5e637b60b27bace002b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 17:59:48 +0100 Subject: [PATCH 120/130] backend-plugin-api: align opaque type markers Signed-off-by: Patrik Oldsberg --- .changeset/itchy-chairs-punch.md | 5 +++++ packages/backend-plugin-api/api-report.md | 4 ++-- packages/backend-plugin-api/src/services/system/types.ts | 4 ++-- .../src/wiring/createSharedEnvironment.test.ts | 2 +- .../backend-plugin-api/src/wiring/createSharedEnvironment.ts | 4 ++-- packages/backend-plugin-api/src/wiring/factories.ts | 2 +- packages/backend-plugin-api/src/wiring/types.ts | 2 +- 7 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 .changeset/itchy-chairs-punch.md diff --git a/.changeset/itchy-chairs-punch.md b/.changeset/itchy-chairs-punch.md new file mode 100644 index 0000000000..034a9bbdae --- /dev/null +++ b/.changeset/itchy-chairs-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +Aligned opaque type markers to all use a `$$type` property with namespacing. diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index a7700d81ac..baa2b1b7dc 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -275,7 +275,7 @@ export type ExtensionPoint = { id: string; T: T; toString(): string; - $$ref: 'extension-point'; + $$type: '@backstage/ExtensionPoint'; }; // @public @@ -490,7 +490,7 @@ export type ServiceRef< scope: TScope; T: TService; toString(): string; - $$ref: 'service'; + $$type: '@backstage/ServiceRef'; }; // @public (undocumented) diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 2615c5e3a3..3e68e9349d 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -44,7 +44,7 @@ export type ServiceRef< toString(): string; - $$ref: 'service'; + $$type: '@backstage/ServiceRef'; }; /** @public */ @@ -119,7 +119,7 @@ export function createServiceRef( toString() { return `serviceRef{${config.id}}`; }, - $$ref: 'service', // TODO: declare + $$type: '@backstage/ServiceRef', __defaultFactory: defaultFactory, } as ServiceRef & { __defaultFactory?: ( diff --git a/packages/backend-plugin-api/src/wiring/createSharedEnvironment.test.ts b/packages/backend-plugin-api/src/wiring/createSharedEnvironment.test.ts index ec2cf1fe49..381a4799d2 100644 --- a/packages/backend-plugin-api/src/wiring/createSharedEnvironment.test.ts +++ b/packages/backend-plugin-api/src/wiring/createSharedEnvironment.test.ts @@ -48,7 +48,7 @@ describe('createSharedEnvironment', () => { expect(env).toBeDefined(); const internalEnv = env() as unknown as InternalSharedBackendEnvironment; expect(internalEnv).toEqual({ - $$type: 'SharedBackendEnvironment', + $$type: '@backstage/SharedBackendEnvironment', version: 'v1', services: undefined, }); diff --git a/packages/backend-plugin-api/src/wiring/createSharedEnvironment.ts b/packages/backend-plugin-api/src/wiring/createSharedEnvironment.ts index 2eb2cdec6a..57f0533523 100644 --- a/packages/backend-plugin-api/src/wiring/createSharedEnvironment.ts +++ b/packages/backend-plugin-api/src/wiring/createSharedEnvironment.ts @@ -31,7 +31,7 @@ export interface SharedBackendEnvironmentConfig { * @public */ export interface SharedBackendEnvironment { - $$type: 'SharedBackendEnvironment'; + $$type: '@backstage/SharedBackendEnvironment'; // NOTE: This type is opaque in order to allow for future API evolution without // cluttering the external API. For example we might want to add support @@ -93,7 +93,7 @@ export function createSharedEnvironment< // Here to ensure type safety in this internal implementation. const env: SharedBackendEnvironment & InternalSharedBackendEnvironment = { - $$type: 'SharedBackendEnvironment', + $$type: '@backstage/SharedBackendEnvironment', version: 'v1', services, }; diff --git a/packages/backend-plugin-api/src/wiring/factories.ts b/packages/backend-plugin-api/src/wiring/factories.ts index d281335a8e..0326080b11 100644 --- a/packages/backend-plugin-api/src/wiring/factories.ts +++ b/packages/backend-plugin-api/src/wiring/factories.ts @@ -55,7 +55,7 @@ export function createExtensionPoint( toString() { return `extensionPoint{${config.id}}`; }, - $$ref: 'extension-point', // TODO: declare + $$type: '@backstage/ExtensionPoint', }; } diff --git a/packages/backend-plugin-api/src/wiring/types.ts b/packages/backend-plugin-api/src/wiring/types.ts index 6e081f57e5..ab8ce07284 100644 --- a/packages/backend-plugin-api/src/wiring/types.ts +++ b/packages/backend-plugin-api/src/wiring/types.ts @@ -32,7 +32,7 @@ export type ExtensionPoint = { toString(): string; - $$ref: 'extension-point'; + $$type: '@backstage/ExtensionPoint'; }; /** From 0d0cb65e5e4ae7cde89a444a236433abf099d22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 19:08:21 +0100 Subject: [PATCH 121/130] Release this change as a patch version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/hip-chairs-double.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/hip-chairs-double.md b/.changeset/hip-chairs-double.md index 722795d80d..52cdde3588 100644 --- a/.changeset/hip-chairs-double.md +++ b/.changeset/hip-chairs-double.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-tech-radar': minor +'@backstage/plugin-tech-radar': patch --- Deprecate `RadarEntry.url` - use `RadarEntry.links` instead From 5b9a1e7dc00a25f474162b73ba606673df23eefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 19:16:04 +0100 Subject: [PATCH 122/130] Make RadarEntry.url optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/tech-radar/api-report.md | 2 +- plugins/tech-radar/src/api.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/tech-radar/api-report.md b/plugins/tech-radar/api-report.md index 2836e04461..3b84990d85 100644 --- a/plugins/tech-radar/api-report.md +++ b/plugins/tech-radar/api-report.md @@ -26,7 +26,7 @@ export interface RadarEntry { timeline: Array; title: string; // @deprecated - url: string; + url?: string; } // @public diff --git a/plugins/tech-radar/src/api.ts b/plugins/tech-radar/src/api.ts index bf90cf0750..c0b8fb8852 100644 --- a/plugins/tech-radar/src/api.ts +++ b/plugins/tech-radar/src/api.ts @@ -136,7 +136,7 @@ export interface RadarEntry { * * @deprecated Use {@link RadarEntry.links} instead */ - url: string; + url?: string; /** * History of the Entry moving through {@link RadarRing} */ From 8060b73e5ed74f053289df62717264f2fd588564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 19:16:27 +0100 Subject: [PATCH 123/130] Remove deprecated url attribute from sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/tech-radar/src/sample.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/tech-radar/src/sample.ts b/plugins/tech-radar/src/sample.ts index 0dc647ac54..1a6f372f64 100644 --- a/plugins/tech-radar/src/sample.ts +++ b/plugins/tech-radar/src/sample.ts @@ -45,7 +45,6 @@ entries.push({ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua', }, ], - url: '#', key: 'javascript', id: 'javascript', title: 'JavaScript', @@ -73,7 +72,6 @@ entries.push({ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat', }, ], - url: '#', key: 'typescript', id: 'typescript', title: 'TypeScript', @@ -91,7 +89,6 @@ entries.push({ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur', }, ], - url: '#', links: [ { url: 'https://webpack.js.org/', @@ -111,7 +108,6 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: '#', links: [ { url: 'https://reactjs.org/', @@ -131,7 +127,6 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: '#', key: 'code-reviews', id: 'code-reviews', title: 'Code Reviews', @@ -145,7 +140,6 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: '#', key: 'mob-programming', id: 'mob-programming', title: 'Mob Programming', @@ -159,7 +153,6 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: '#', key: 'docs-like-code', id: 'docs-like-code', title: 'Docs-like-code', @@ -172,7 +165,6 @@ entries.push({ date: new Date('2020-08-06'), }, ], - url: '#', key: 'force-push', id: 'force-push', title: 'Force push to master', @@ -186,7 +178,6 @@ entries.push({ description: 'long description', }, ], - url: '#', links: [ { url: 'https://reactjs.org/', From 8784b505af2ccef161a479ce3b099468e8c45a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 19:17:51 +0100 Subject: [PATCH 124/130] Remove url attribute from the diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/hip-chairs-double.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/hip-chairs-double.md b/.changeset/hip-chairs-double.md index 52cdde3588..e2b03f2e66 100644 --- a/.changeset/hip-chairs-double.md +++ b/.changeset/hip-chairs-double.md @@ -6,7 +6,6 @@ Deprecate `RadarEntry.url` - use `RadarEntry.links` instead ```diff - url: 'https://www.javascript.com/', -+ url: '#', key: 'javascript', id: 'javascript', title: 'JavaScript', From 1cea9544d15fd6061339713db8f1b43c8124fd7e Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Mon, 6 Feb 2023 19:44:31 +0100 Subject: [PATCH 125/130] Update silly-buckets-joke.md Signed-off-by: Ben Lambert --- .changeset/silly-buckets-joke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/silly-buckets-joke.md b/.changeset/silly-buckets-joke.md index bd77f2ed05..113272c965 100644 --- a/.changeset/silly-buckets-joke.md +++ b/.changeset/silly-buckets-joke.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend-module-msgraph': patch --- -Fixed bug in readProviderConfig as it wasn't reading the user.select field expected from the app-config.yaml configuration +Fixed a bug reading the `user.select` field expected from the `app-config.yaml` configuration From 67ebd7f8a576638d34d37e184efc0abd6a2cfb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 6 Feb 2023 19:45:14 +0100 Subject: [PATCH 126/130] Fix URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/tech-radar/src/sample.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tech-radar/src/sample.ts b/plugins/tech-radar/src/sample.ts index 1a6f372f64..395b4f2ff5 100644 --- a/plugins/tech-radar/src/sample.ts +++ b/plugins/tech-radar/src/sample.ts @@ -180,7 +180,7 @@ entries.push({ ], links: [ { - url: 'https://reactjs.org/', + url: 'https://github.com', title: 'Learn more', }, ], From 95433b19875cf3070349bbaf8829ae1e160623c4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Feb 2023 19:55:21 +0100 Subject: [PATCH 127/130] Apply suggestions from code review Co-authored-by: Ben Lambert Signed-off-by: Patrik Oldsberg --- .changeset/little-jars-lay.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.changeset/little-jars-lay.md b/.changeset/little-jars-lay.md index 161dc099e9..f8851f7adc 100644 --- a/.changeset/little-jars-lay.md +++ b/.changeset/little-jars-lay.md @@ -1,10 +1,6 @@ --- -'@internal/plugin-todo-list-backend': patch '@backstage/plugin-user-settings-backend': patch -'@backstage/backend-test-utils': patch '@backstage/plugin-scaffolder-backend': patch -'@backstage/backend-defaults': patch -'@backstage/backend-app-api': patch '@backstage/plugin-periskop-backend': patch '@backstage/backend-common': patch '@backstage/plugin-catalog-backend': patch From c1a19d2647b391e5565e442bf3f6dc89cf0a8164 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 6 Feb 2023 21:05:07 +0100 Subject: [PATCH 128/130] chore: fix api-reports Signed-off-by: blam --- packages/backend-plugin-api/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index baa2b1b7dc..a8174c042c 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -508,7 +508,7 @@ export interface ServiceRefConfig { // @public export interface SharedBackendEnvironment { // (undocumented) - $$type: 'SharedBackendEnvironment'; + $$type: '@backstage/SharedBackendEnvironment'; } // @public From 65ec06e9e7b76080a48b5e19d78688eb0edf0cdb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 20:31:49 +0000 Subject: [PATCH 129/130] chore(deps): update dependency esbuild to v0.17.6 Signed-off-by: Renovate Bot --- yarn.lock | 182 +++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/yarn.lock b/yarn.lock index c9caa42b95..de12cf4659 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9231,9 +9231,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/android-arm64@npm:0.17.5" +"@esbuild/android-arm64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/android-arm64@npm:0.17.6" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -9245,65 +9245,65 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/android-arm@npm:0.17.5" +"@esbuild/android-arm@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/android-arm@npm:0.17.6" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/android-x64@npm:0.17.5" +"@esbuild/android-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/android-x64@npm:0.17.6" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/darwin-arm64@npm:0.17.5" +"@esbuild/darwin-arm64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/darwin-arm64@npm:0.17.6" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/darwin-x64@npm:0.17.5" +"@esbuild/darwin-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/darwin-x64@npm:0.17.6" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/freebsd-arm64@npm:0.17.5" +"@esbuild/freebsd-arm64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/freebsd-arm64@npm:0.17.6" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/freebsd-x64@npm:0.17.5" +"@esbuild/freebsd-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/freebsd-x64@npm:0.17.6" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-arm64@npm:0.17.5" +"@esbuild/linux-arm64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-arm64@npm:0.17.6" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-arm@npm:0.17.5" +"@esbuild/linux-arm@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-arm@npm:0.17.6" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-ia32@npm:0.17.5" +"@esbuild/linux-ia32@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-ia32@npm:0.17.6" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -9315,86 +9315,86 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-loong64@npm:0.17.5" +"@esbuild/linux-loong64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-loong64@npm:0.17.6" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-mips64el@npm:0.17.5" +"@esbuild/linux-mips64el@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-mips64el@npm:0.17.6" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-ppc64@npm:0.17.5" +"@esbuild/linux-ppc64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-ppc64@npm:0.17.6" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-riscv64@npm:0.17.5" +"@esbuild/linux-riscv64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-riscv64@npm:0.17.6" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-s390x@npm:0.17.5" +"@esbuild/linux-s390x@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-s390x@npm:0.17.6" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/linux-x64@npm:0.17.5" +"@esbuild/linux-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/linux-x64@npm:0.17.6" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/netbsd-x64@npm:0.17.5" +"@esbuild/netbsd-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/netbsd-x64@npm:0.17.6" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/openbsd-x64@npm:0.17.5" +"@esbuild/openbsd-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/openbsd-x64@npm:0.17.6" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/sunos-x64@npm:0.17.5" +"@esbuild/sunos-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/sunos-x64@npm:0.17.6" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/win32-arm64@npm:0.17.5" +"@esbuild/win32-arm64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/win32-arm64@npm:0.17.6" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/win32-ia32@npm:0.17.5" +"@esbuild/win32-ia32@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/win32-ia32@npm:0.17.6" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.17.5": - version: 0.17.5 - resolution: "@esbuild/win32-x64@npm:0.17.5" +"@esbuild/win32-x64@npm:0.17.6": + version: 0.17.6 + resolution: "@esbuild/win32-x64@npm:0.17.6" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -21470,31 +21470,31 @@ __metadata: linkType: hard "esbuild@npm:^0.17.0": - version: 0.17.5 - resolution: "esbuild@npm:0.17.5" + version: 0.17.6 + resolution: "esbuild@npm:0.17.6" dependencies: - "@esbuild/android-arm": 0.17.5 - "@esbuild/android-arm64": 0.17.5 - "@esbuild/android-x64": 0.17.5 - "@esbuild/darwin-arm64": 0.17.5 - "@esbuild/darwin-x64": 0.17.5 - "@esbuild/freebsd-arm64": 0.17.5 - "@esbuild/freebsd-x64": 0.17.5 - "@esbuild/linux-arm": 0.17.5 - "@esbuild/linux-arm64": 0.17.5 - "@esbuild/linux-ia32": 0.17.5 - "@esbuild/linux-loong64": 0.17.5 - "@esbuild/linux-mips64el": 0.17.5 - "@esbuild/linux-ppc64": 0.17.5 - "@esbuild/linux-riscv64": 0.17.5 - "@esbuild/linux-s390x": 0.17.5 - "@esbuild/linux-x64": 0.17.5 - "@esbuild/netbsd-x64": 0.17.5 - "@esbuild/openbsd-x64": 0.17.5 - "@esbuild/sunos-x64": 0.17.5 - "@esbuild/win32-arm64": 0.17.5 - "@esbuild/win32-ia32": 0.17.5 - "@esbuild/win32-x64": 0.17.5 + "@esbuild/android-arm": 0.17.6 + "@esbuild/android-arm64": 0.17.6 + "@esbuild/android-x64": 0.17.6 + "@esbuild/darwin-arm64": 0.17.6 + "@esbuild/darwin-x64": 0.17.6 + "@esbuild/freebsd-arm64": 0.17.6 + "@esbuild/freebsd-x64": 0.17.6 + "@esbuild/linux-arm": 0.17.6 + "@esbuild/linux-arm64": 0.17.6 + "@esbuild/linux-ia32": 0.17.6 + "@esbuild/linux-loong64": 0.17.6 + "@esbuild/linux-mips64el": 0.17.6 + "@esbuild/linux-ppc64": 0.17.6 + "@esbuild/linux-riscv64": 0.17.6 + "@esbuild/linux-s390x": 0.17.6 + "@esbuild/linux-x64": 0.17.6 + "@esbuild/netbsd-x64": 0.17.6 + "@esbuild/openbsd-x64": 0.17.6 + "@esbuild/sunos-x64": 0.17.6 + "@esbuild/win32-arm64": 0.17.6 + "@esbuild/win32-ia32": 0.17.6 + "@esbuild/win32-x64": 0.17.6 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -21542,7 +21542,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 31829b46a0cad65f58d90d39b66fd32a2de820539dd8530199975e417aa0ecd63cfc340e402f4f4e9235906a1a05969ddac1d4ad1ba611a3f11c0f62516b9390 + checksum: 13c4c3bcaa7ff291810d2c8294a1eb997b4672c60a9285b32d8eafcbc552e4468e06efe9d1a15067f5cbd41adcb12ddb4362618845e2e916e437f281c9aa80ab languageName: node linkType: hard From 546bdf30b38adbe972d39c9239d6b80333977be0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 20:33:02 +0000 Subject: [PATCH 130/130] fix(deps): update dependency @graphql-tools/schema to v9.0.16 Signed-off-by: Renovate Bot --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index c9caa42b95..a3e3cdfcbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10125,15 +10125,15 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:8.3.17, @graphql-tools/merge@npm:^8.2.6": - version: 8.3.17 - resolution: "@graphql-tools/merge@npm:8.3.17" +"@graphql-tools/merge@npm:8.3.18, @graphql-tools/merge@npm:^8.2.6": + version: 8.3.18 + resolution: "@graphql-tools/merge@npm:8.3.18" dependencies: - "@graphql-tools/utils": 9.2.0 + "@graphql-tools/utils": 9.2.1 tslib: ^2.4.0 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 943e1ad64575ab9ae6726d434cf7b08e80099442f9d25c1ac9f11fdac7bd0e8e73109f0e2469b47650ad0a0731ac850ede844b5ff8a7eab352027a42c1e78c79 + checksum: 0834fcf13dec5ee92e39f2e1b27a098654e2db20bf6ef6f43d7efce56b7d750caa59dd16aacb211b8f5a198d29c19d36471401762f5a34e0bc22657285dcae92 languageName: node linkType: hard @@ -10261,16 +10261,16 @@ __metadata: linkType: hard "@graphql-tools/schema@npm:^9.0.0": - version: 9.0.15 - resolution: "@graphql-tools/schema@npm:9.0.15" + version: 9.0.16 + resolution: "@graphql-tools/schema@npm:9.0.16" dependencies: - "@graphql-tools/merge": 8.3.17 - "@graphql-tools/utils": 9.2.0 + "@graphql-tools/merge": 8.3.18 + "@graphql-tools/utils": 9.2.1 tslib: ^2.4.0 value-or-promise: 1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 4361cb1a355e8811fd9b762bd137a2f8fd287b05f03cb574bc3d5e6855aeceef462e72bacfa89dd9028138e9fe3cafc4d69c95d7f9a9b6ab7f037ca3e36f52a9 + checksum: ecbd27b4a36424c8d4b2206b1f2d2ec2cff3c66f52ec452b17977642017ca7f00df4df44f2d0426174ceea9596474d0656bc0227f4c1149f2288abb6d186cd68 languageName: node linkType: hard @@ -10363,15 +10363,15 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:9.2.0, @graphql-tools/utils@npm:^9.0.0, @graphql-tools/utils@npm:^9.1.1": - version: 9.2.0 - resolution: "@graphql-tools/utils@npm:9.2.0" +"@graphql-tools/utils@npm:9.2.1, @graphql-tools/utils@npm:^9.0.0, @graphql-tools/utils@npm:^9.1.1": + version: 9.2.1 + resolution: "@graphql-tools/utils@npm:9.2.1" dependencies: "@graphql-typed-document-node/core": ^3.1.1 tslib: ^2.4.0 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 9754f8c6d52490df395626cef224542f37ca040ced6688c8ad80b1e0498006186529a50ac21979465123debba093db33dbfe8f1f7a9101f969dec0f31dc9a45b + checksum: 94ed12df5f49e5c338322ffd931236a687a3d5c443bf499f9baab5d4fcd9792234111142be8aa506a01ca2e82732996c4e1d8f6159ff9cc7fdc5c97f63e55226 languageName: node linkType: hard