From d8d243ca229a34f7bed3b3aee04d59c29df25f3c Mon Sep 17 00:00:00 2001 From: Gabriel Pereira Woitechen Date: Tue, 2 Jan 2024 15:24:45 -0300 Subject: [PATCH 01/39] fix(techdocs-cli): mkdocs parameter casing Signed-off-by: Gabriel Pereira Woitechen --- .changeset/selfish-mails-scream.md | 5 +++++ packages/techdocs-cli/src/commands/serve/serve.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/selfish-mails-scream.md diff --git a/.changeset/selfish-mails-scream.md b/.changeset/selfish-mails-scream.md new file mode 100644 index 0000000000..7d1149f27a --- /dev/null +++ b/.changeset/selfish-mails-scream.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +fix: mkdocs parameter casing diff --git a/packages/techdocs-cli/src/commands/serve/serve.ts b/packages/techdocs-cli/src/commands/serve/serve.ts index de681c2de4..27f432c22d 100644 --- a/packages/techdocs-cli/src/commands/serve/serve.ts +++ b/packages/techdocs-cli/src/commands/serve/serve.ts @@ -117,7 +117,7 @@ export default async function serve(opts: OptionValues) { stderrLogFunc: mkdocsLogFunc, mkdocsConfigFileName: mkdocsYmlPath, mkdocsParameterClean: opts.mkdocsParameterClean, - mkdocsParameterDirtyReload: opts.mkdocsParameterDirtyReload, + mkdocsParameterDirtyReload: opts.mkdocsParameterDirtyreload, mkdocsParameterStrict: opts.mkdocsParameterStrict, }); From e27b7f32072b96c6894f020f343d0349125803e3 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 15 Jan 2024 15:39:13 +0100 Subject: [PATCH 02/39] fix(Github): use `x-ratelimit-remaining` and 429 status code for rate limit detection Signed-off-by: secustor --- .changeset/sweet-ravens-glow.md | 6 +++++ .../src/reading/GithubUrlReader.ts | 15 +++++++---- .../src/github/GithubIntegration.test.ts | 25 +++++++++++++++++++ .../src/github/GithubIntegration.ts | 8 ++++++ 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 .changeset/sweet-ravens-glow.md diff --git a/.changeset/sweet-ravens-glow.md b/.changeset/sweet-ravens-glow.md new file mode 100644 index 0000000000..045f7edbf2 --- /dev/null +++ b/.changeset/sweet-ravens-glow.md @@ -0,0 +1,6 @@ +--- +'@backstage/integration': minor +'@backstage/backend-common': patch +--- + +Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 764431386d..83c21b2529 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -149,10 +149,7 @@ export class GithubUrlReader implements UrlReader { // GitHub returns a 403 response with a couple of headers indicating rate // limit status. See more in the GitHub docs: // https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting - if ( - response.status === 403 && - response.headers.get('X-RateLimit-Remaining') === '0' - ) { + if (this.integration.isRateLimited(response)) { message += ' (rate limit exceeded)'; } @@ -350,10 +347,18 @@ export class GithubUrlReader implements UrlReader { const response = await fetch(urlAsString, init); if (!response.ok) { - const message = `Request failed for ${urlAsString}, ${response.status} ${response.statusText}`; + let message = `Request failed for ${urlAsString}, ${response.status} ${response.statusText}`; if (response.status === 404) { throw new NotFoundError(message); } + + // GitHub returns a 403 response with a couple of headers indicating rate + // limit status. See more in the GitHub docs: + // https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting + if (this.integration.isRateLimited(response)) { + message += ' (rate limit exceeded)'; + } + throw new Error(message); } diff --git a/packages/integration/src/github/GithubIntegration.test.ts b/packages/integration/src/github/GithubIntegration.test.ts index d8e6020a64..36c802da52 100644 --- a/packages/integration/src/github/GithubIntegration.test.ts +++ b/packages/integration/src/github/GithubIntegration.test.ts @@ -78,6 +78,31 @@ describe('GithubIntegration', () => { ), ).toBe('https://github.com/backstage/backstage/edit/master/README.md'); }); + + describe('isRateLimited', () => { + const integration = new GithubIntegration({ host: 'h.com' }); + + it.each` + status | ratelimitRemaining | expected + ${404} | ${100} | ${false} + ${429} | ${undefined} | ${true} + ${429} | ${100} | ${true} + ${403} | ${100} | ${false} + ${403} | ${0} | ${true} + `( + '(statusCode: $status, header: $ratelimitRemaining) === $expected', + ({ status, ratelimitRemaining, expected }) => { + const headers = new Headers({ + 'x-ratelimit-remaining': ratelimitRemaining, + }); + const result = integration.isRateLimited({ + status, + headers, + } as Response); + expect(expected).toBe(result); + }, + ); + }); }); describe('replaceGithubUrlType', () => { diff --git a/packages/integration/src/github/GithubIntegration.ts b/packages/integration/src/github/GithubIntegration.ts index 50fc129ccf..e2b163dc2d 100644 --- a/packages/integration/src/github/GithubIntegration.ts +++ b/packages/integration/src/github/GithubIntegration.ts @@ -65,6 +65,14 @@ export class GithubIntegration implements ScmIntegration { resolveEditUrl(url: string): string { return replaceGithubUrlType(url, 'edit'); } + + isRateLimited(response: Response): boolean { + return ( + response.status === 429 || + (response.status === 403 && + response.headers.get('x-ratelimit-remaining') === '0') + ); + } } /** From 633250f0e5b8023c19492b4ab351b51bd3ab0313 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 15 Jan 2024 16:03:43 +0100 Subject: [PATCH 03/39] chore: use ConsumedResponse to pass eslint validation Signed-off-by: secustor --- packages/integration/src/github/GithubIntegration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/integration/src/github/GithubIntegration.ts b/packages/integration/src/github/GithubIntegration.ts index e2b163dc2d..7a6ac5675d 100644 --- a/packages/integration/src/github/GithubIntegration.ts +++ b/packages/integration/src/github/GithubIntegration.ts @@ -20,6 +20,7 @@ import { GithubIntegrationConfig, readGithubIntegrationConfigs, } from './config'; +import { ConsumedResponse } from '@backstage/errors'; /** * A GitHub based integration. @@ -66,7 +67,7 @@ export class GithubIntegration implements ScmIntegration { return replaceGithubUrlType(url, 'edit'); } - isRateLimited(response: Response): boolean { + isRateLimited(response: ConsumedResponse): boolean { return ( response.status === 429 || (response.status === 403 && From 7120dfcb871c815955ad6c709b35a98fc93e5618 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 15 Jan 2024 16:11:30 +0100 Subject: [PATCH 04/39] fixup! chore: use ConsumedResponse to pass eslint validation Signed-off-by: secustor --- packages/integration/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/integration/package.json b/packages/integration/package.json index 7e57293903..29a4273698 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -35,6 +35,7 @@ "dependencies": { "@azure/identity": "^4.0.0", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@octokit/auth-app": "^4.0.0", "@octokit/rest": "^19.0.3", "cross-fetch": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index 230f6d3e8d..7740ad7363 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4285,6 +4285,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/config-loader": "workspace:^" + "@backstage/errors": "workspace:^" "@octokit/auth-app": ^4.0.0 "@octokit/rest": ^19.0.3 "@types/luxon": ^3.0.0 From 626e790ec9c2b7f5b1b368e24d66dcaa08098a58 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 15 Jan 2024 16:15:44 +0100 Subject: [PATCH 05/39] fixup! chore: use ConsumedResponse to pass eslint validation Signed-off-by: secustor --- packages/integration/api-report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 634e03dbfc..f830437cf1 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -4,6 +4,7 @@ ```ts import { Config } from '@backstage/config'; +import { ConsumedResponse } from '@backstage/errors'; import { RestEndpointMethodTypes } from '@octokit/rest'; // @public @@ -567,6 +568,8 @@ export class GithubIntegration implements ScmIntegration { // (undocumented) static factory: ScmIntegrationsFactory; // (undocumented) + isRateLimited(response: ConsumedResponse): boolean; + // (undocumented) resolveEditUrl(url: string): string; // (undocumented) resolveUrl(options: { From b53b569b51a6b226322bd1da192df08d3c727e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Fri, 12 Jan 2024 12:59:15 +0000 Subject: [PATCH 06/39] addded condition to prevent previous users and groups from being wiped if sync request fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../providers/GithubMultiOrgEntityProvider.ts | 29 ++++++++++-------- .../src/providers/GithubOrgEntityProvider.ts | 30 +++++++++++-------- .../src/processors/LdapOrgEntityProvider.ts | 23 +++++++------- .../MicrosoftGraphOrgEntityProvider.ts | 23 +++++++------- 4 files changed, 59 insertions(+), 46 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index cabb9e21ce..858095e766 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -298,21 +298,24 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { } const allUsers = Array.from(allUsersMap.values()); + if (allUsers.length > 0 || allTeams.length > 0) { + const { markCommitComplete } = markReadComplete({ allUsers, allTeams }); - const { markCommitComplete } = markReadComplete({ allUsers, allTeams }); + await this.connection.applyMutation({ + type: 'full', + entities: [...allUsers, ...allTeams].map(entity => ({ + locationKey: `github-multi-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + entity, + ), + })), + }); - await this.connection.applyMutation({ - type: 'full', - entities: [...allUsers, ...allTeams].map(entity => ({ - locationKey: `github-multi-org-provider:${this.options.id}`, - entity: withLocations( - `https://${this.options.gitHubConfig.host}`, - entity, - ), - })), - }); - - markCommitComplete(); + markCommitComplete(); + } else { + logger.info('No users or teams to process'); + } } private supportsEventTopics(): string[] { diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index 0fe61aa229..a3ff980fd6 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -229,21 +229,25 @@ export class GithubOrgEntityProvider } } - const { markCommitComplete } = markReadComplete({ users, teams }); + if (users.length > 0 || teams.length > 0) { + const { markCommitComplete } = markReadComplete({ users, teams }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...teams].map(entity => ({ - locationKey: `github-org-provider:${this.options.id}`, - entity: withLocations( - `https://${this.options.gitHubConfig.host}`, - org, - entity, - ), - })), - }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...teams].map(entity => ({ + locationKey: `github-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + org, + entity, + ), + })), + }); - markCommitComplete(); + markCommitComplete(); + } else { + logger.info('No users or teams to process'); + } } /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.onEvent} */ diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts index d4f19d0522..ced28face8 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts @@ -192,18 +192,21 @@ export class LdapOrgEntityProvider implements EntityProvider { logger, }, ); + if (users.length > 0 || groups.length > 0) { + const { markCommitComplete } = markReadComplete({ users, groups }); - const { markCommitComplete } = markReadComplete({ users, groups }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...groups].map(entity => ({ + locationKey: `ldap-org-provider:${this.options.id}`, + entity: withLocations(this.options.id, entity), + })), + }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...groups].map(entity => ({ - locationKey: `ldap-org-provider:${this.options.id}`, - entity: withLocations(this.options.id, entity), - })), - }); - - markCommitComplete(); + markCommitComplete(); + } else { + logger.info('No users or teams to process'); + } } private schedule(schedule: LdapOrgEntityProviderOptions['schedule']) { diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 160696e2e7..3a3d5c29c4 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -323,18 +323,21 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { logger: logger, }, ); + if (users.length > 0 || groups.length > 0) { + const { markCommitComplete } = markReadComplete({ users, groups }); - const { markCommitComplete } = markReadComplete({ users, groups }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...groups].map(entity => ({ + locationKey: `msgraph-org-provider:${this.options.id}`, + entity: withLocations(this.options.id, entity), + })), + }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...groups].map(entity => ({ - locationKey: `msgraph-org-provider:${this.options.id}`, - entity: withLocations(this.options.id, entity), - })), - }); - - markCommitComplete(); + markCommitComplete(); + } else { + logger.info('No users or teams to process'); + } } private schedule(taskRunner: TaskRunner) { From d01626d0051ddddc4b5316124b6858d3339a3221 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:45:44 +0000 Subject: [PATCH 07/39] fix(deps): update typescript-eslint monorepo to v6.19.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33ac9d7aab..76dc8030fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19282,14 +19282,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.12.0": - version: 6.19.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.19.0" + version: 6.19.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.19.0 - "@typescript-eslint/type-utils": 6.19.0 - "@typescript-eslint/utils": 6.19.0 - "@typescript-eslint/visitor-keys": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/type-utils": 6.19.1 + "@typescript-eslint/utils": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 @@ -19302,25 +19302,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 9880567d52d4e6559e2343caeed68f856d593b42816b8f705cd98d5a5b46cc620e3bebaaf08bbc982061bba18e5be94d6c539c0c816e8772ddabba0ad4e9363e + checksum: ad04000cd6c15d864ff92655baa3aec99bb0ccf4714fedd145fedde60a27590a5feafe480beb2f0f3864b416098bde1e9431bada7480eb7ca4efad891e1d2f6f languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.7.2": - version: 6.19.0 - resolution: "@typescript-eslint/parser@npm:6.19.0" + version: 6.19.1 + resolution: "@typescript-eslint/parser@npm:6.19.1" dependencies: - "@typescript-eslint/scope-manager": 6.19.0 - "@typescript-eslint/types": 6.19.0 - "@typescript-eslint/typescript-estree": 6.19.0 - "@typescript-eslint/visitor-keys": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 0ac91ff83fdf693de4494b45be79f25803ea6ca3ee717e4f96785b7ffc1da0180adb0426b61bc6eff5666c8ef9ea58c50efbd4351ef9018c0050116cbd74a62b + checksum: cd29619da08a2d9b7123ba4d8240989c747f8e0d5672179d8b147e413ee1334d1fa48570b0c37cf0ae4e26a275fd2d268cbe702c6fed639d3331abbb3292570a languageName: node linkType: hard @@ -19334,22 +19334,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/scope-manager@npm:6.19.0" +"@typescript-eslint/scope-manager@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/scope-manager@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.19.0 - "@typescript-eslint/visitor-keys": 6.19.0 - checksum: 47d9d1b70cd64f9d1bb717090850e0ff1a34e453c28b43fd0cecaea4db05cacebd60f5da55b35c4b3cc01491f02e9de358f82a0822b27c00e80e3d1a27de32d1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 + checksum: 848cdebc16a3803e8a6d6035a7067605309a652bb2425f475f755b5ace4d80d2c17c8c8901f0f4759556da8d0a5b71024d472b85c3f3c70d0e6dcfe2a972ef35 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/type-utils@npm:6.19.0" +"@typescript-eslint/type-utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/type-utils@npm:6.19.1" dependencies: - "@typescript-eslint/typescript-estree": 6.19.0 - "@typescript-eslint/utils": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/utils": 6.19.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -19357,7 +19357,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: a88f022617be636f43429a7c7c5cd2e0e29955e96d4a9fed7d03467dc4a432b1240a71009d62213604ddb3522be9694e6b78882ee805687cda107021d1ddb203 + checksum: eab1a30f8d85f7c6e2545de5963fbec2f3bb91913d59623069b4b0db372a671ab048c7018376fc853c3af06ea39417f3e7b27dd665027dd812347a5e64cecd77 languageName: node linkType: hard @@ -19368,10 +19368,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/types@npm:6.19.0" - checksum: 1371b5ba41c1d2879b3c2823ab01a30cf034e476ef53ff2a7f9e9a4a0056dfbbfecd3143031b05430aa6c749233cacbd01b72cea38a9ece1c6cf95a5cd43da6a +"@typescript-eslint/types@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/types@npm:6.19.1" + checksum: 598ce222b59c20432d06f60703d0c2dd16d9b2151569c192852136c57b8188e3ef6ef9fddaa2c136c9a756fcc7d873c0e29ec41cfd340564842287ef7b4571cd languageName: node linkType: hard @@ -19393,12 +19393,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.0" +"@typescript-eslint/typescript-estree@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.19.0 - "@typescript-eslint/visitor-keys": 6.19.0 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -19408,24 +19408,24 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 919f9588840cdab7e0ef6471f4c35d602523b142b2cffeabe9171d6ce65eb7f41614d0cb17e008e0d8e796374821ab053ced35b84642c3b1d491987362f2fdb5 + checksum: fb71a14aeee0468780219c5b8d39075f85d360b04ccd0ee88f4f0a615d2c232a6d3016e36d8c6eda2d9dfda86b4f4cc2c3d7582940fb29d33c7cf305e124d4e2 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/utils@npm:6.19.0" +"@typescript-eslint/utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/utils@npm:6.19.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.19.0 - "@typescript-eslint/types": 6.19.0 - "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 05a26251a526232b08850b6c3327637213ef989453e353f3a8255433b74893a70d5c38369c528b762e853b7586d7830d728b372494e65f37770ecb05a88112d4 + checksum: fe72e75c3ea17a85772b83f148555ea94ff5d55d13586f3fc038833197a74f8071e14c2bbf1781c40eec20005f052f4be2513a725eea82a15da3cb9af3046c70 languageName: node linkType: hard @@ -19457,13 +19457,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.19.0": - version: 6.19.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.19.0" +"@typescript-eslint/visitor-keys@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/types": 6.19.1 eslint-visitor-keys: ^3.4.1 - checksum: 35b11143e1b55ecf01e0f513085df2cc83d0781f4a8354dc10f6ec3356f66b91a1ed8abadb6fb66af1c1746f9c874eabc8b5636882466e229cda5d6a39aada08 + checksum: bdf057a42e776970a89cdd568e493e3ea7ec085544d8f318d33084da63c3395ad2c0fb9cef9f61ceeca41f5dab54ab064b7078fe596889005e412ec74d2d1ae4 languageName: node linkType: hard From 01628752ed4605ed66cb1f72e548e611dfc87f19 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 23:03:27 +0000 Subject: [PATCH 08/39] fix(deps): update dependency @azure/identity to v4.0.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33ac9d7aab..b01e34833c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1547,15 +1547,15 @@ __metadata: linkType: hard "@azure/identity@npm:^4.0.0": - version: 4.0.0 - resolution: "@azure/identity@npm:4.0.0" + version: 4.0.1 + resolution: "@azure/identity@npm:4.0.1" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.5.0 "@azure/core-client": ^1.4.0 "@azure/core-rest-pipeline": ^1.1.0 "@azure/core-tracing": ^1.0.0 - "@azure/core-util": ^1.0.0 + "@azure/core-util": ^1.3.0 "@azure/logger": ^1.0.0 "@azure/msal-browser": ^3.5.0 "@azure/msal-node": ^2.5.1 @@ -1564,7 +1564,7 @@ __metadata: open: ^8.0.0 stoppable: ^1.1.0 tslib: ^2.2.0 - checksum: 534a62afe4715d18e221e021f8088873b1efad34344bd4c6c4685572c89517f15a646e246e9233e8db7942b0a5c73f1961ea63e3baf39c28edde1ba51da4423d + checksum: 2c975ca70b274dc185022c2b19f1774079fd5cfb08c78ec3500e4baf973911b2958031d5fa36369d3c9acdeb25db457cc497991e7393b383103c058e097703e5 languageName: node linkType: hard From 00c90984e5af4c0e1b0f60c641210d529e332e22 Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Tue, 23 Jan 2024 16:49:16 +0100 Subject: [PATCH 09/39] add possibility to configure nunjucks configs Signed-off-by: Kiss Miklos --- .../src/lib/templating/SecureTemplater.ts | 7 +++++++ .../src/scaffolder/actions/builtin/fetch/template.ts | 6 ++++++ plugins/scaffolder-node/src/index.ts | 2 +- plugins/scaffolder-node/src/types.ts | 3 +++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index 701863c7f6..8323b4aa98 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -19,6 +19,7 @@ import { resolvePackagePath } from '@backstage/backend-common'; import { TemplateFilter as _TemplateFilter, TemplateGlobal as _TemplateGlobal, + NunjucksConfigs, } from '@backstage/plugin-scaffolder-node'; import fs from 'fs-extra'; import { JsonValue } from '@backstage/types'; @@ -34,6 +35,7 @@ const { render, renderCompat } = (() => { const env = module.exports.configure({ autoescape: false, + ...JSON.parse(nunjucksConfigs), tags: { variableStart: '\${{', variableEnd: '}}', @@ -42,6 +44,7 @@ const { render, renderCompat } = (() => { const compatEnv = module.exports.configure({ autoescape: false, + ...JSON.parse(nunjucksConfigs), tags: { variableStart: '{{', variableEnd: '}}', @@ -109,6 +112,7 @@ export interface SecureTemplaterOptions { templateFilters?: Record; /* Extra user-provided nunjucks globals */ templateGlobals?: Record; + nunjucksConfigs?: NunjucksConfigs; } export type SecureTemplateRenderer = ( @@ -122,6 +126,7 @@ export class SecureTemplater { cookiecutterCompat, templateFilters = {}, templateGlobals = {}, + nunjucksConfigs = {}, } = options; const isolate = new Isolate({ memoryLimit: 128 }); @@ -140,6 +145,8 @@ export class SecureTemplater { mkScript(nunjucksSource), ); + await contextGlobal.set('nunjucksConfigs', JSON.stringify(nunjucksConfigs)); + const availableFilters = Object.keys(templateFilters); await contextGlobal.set( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index 26b249b76d..2a2c138708 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -69,6 +69,8 @@ export function createFetchTemplateAction(options: { copyWithoutTemplating?: string[]; cookiecutterCompat?: boolean; replace?: boolean; + trimBlocks?: boolean; + lstripBlocks?: boolean; }>({ id: 'fetch:template', description: @@ -237,6 +239,10 @@ export function createFetchTemplateAction(options: { ...additionalTemplateFilters, }, templateGlobals: additionalTemplateGlobals, + nunjucksConfigs: { + trimBlocks: ctx.input.trimBlocks, + lstripBlocks: ctx.input.lstripBlocks, + }, }); for (const location of allEntriesInTemplate) { diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index 0ec492dd32..a049ffd9b7 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -23,4 +23,4 @@ export * from './actions'; export * from './tasks'; export * from './files'; -export type { TemplateFilter, TemplateGlobal } from './types'; +export type { TemplateFilter, TemplateGlobal, NunjucksConfigs } from './types'; diff --git a/plugins/scaffolder-node/src/types.ts b/plugins/scaffolder-node/src/types.ts index bae106c84e..892570af80 100644 --- a/plugins/scaffolder-node/src/types.ts +++ b/plugins/scaffolder-node/src/types.ts @@ -23,3 +23,6 @@ export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; export type TemplateGlobal = | ((...args: JsonValue[]) => JsonValue | undefined) | JsonValue; + +/** @public */ +export type NunjucksConfigs = { trimBlocks?: boolean; lstripBlocks?: boolean }; From 3800d4a8ad1ee3f7109fa1a95d8e980050fa7cad Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Wed, 24 Jan 2024 11:42:10 +0100 Subject: [PATCH 10/39] add api-reports Signed-off-by: Kiss Miklos --- plugins/scaffolder-backend/api-report.md | 2 ++ plugins/scaffolder-node/api-report.md | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index c928e5341e..aa8097033e 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -175,6 +175,8 @@ export function createFetchTemplateAction(options: { copyWithoutTemplating?: string[] | undefined; cookiecutterCompat?: boolean | undefined; replace?: boolean | undefined; + trimBlocks?: boolean | undefined; + lstripBlocks?: boolean | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 6bb1534679..877f979656 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -163,6 +163,12 @@ export function initRepoAndPush(input: { commitHash: string; }>; +// @public (undocumented) +export type NunjucksConfigs = { + trimBlocks?: boolean; + lstripBlocks?: boolean; +}; + // @public (undocumented) export const parseRepoUrl: ( repoUrl: string, From e0e5afeaf614ae69c73948d76b60def09a2101b9 Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Wed, 24 Jan 2024 11:44:03 +0100 Subject: [PATCH 11/39] changeset Signed-off-by: Kiss Miklos --- .changeset/young-ladybugs-decide.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/young-ladybugs-decide.md diff --git a/.changeset/young-ladybugs-decide.md b/.changeset/young-ladybugs-decide.md new file mode 100644 index 0000000000..53284cbc37 --- /dev/null +++ b/.changeset/young-ladybugs-decide.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action From 010b144b6dc34b8f39cce034c0846aa50e8a0ad9 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 24 Jan 2024 14:12:33 +0100 Subject: [PATCH 12/39] Remove outdated initiatives Signed-off-by: Philipp Hugenroth --- microsite/docusaurus.config.js | 2 +- microsite/src/pages/community/index.tsx | 65 +------------------ microsite/src/pages/nominate/index.tsx | 27 -------- .../src/pages/nominate/nominate.module.scss | 32 --------- 4 files changed, 2 insertions(+), 124 deletions(-) delete mode 100644 microsite/src/pages/nominate/index.tsx delete mode 100644 microsite/src/pages/nominate/nominate.module.scss diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js index f07f9da6af..df2833b3e2 100644 --- a/microsite/docusaurus.config.js +++ b/microsite/docusaurus.config.js @@ -277,7 +277,7 @@ module.exports = { to: 'https://developer.spotify.com/', }, { - label: 'Github', + label: 'GitHub', to: 'https://github.com/backstage/', }, ], diff --git a/microsite/src/pages/community/index.tsx b/microsite/src/pages/community/index.tsx index 9b10183ea4..dbc2c4b5e1 100644 --- a/microsite/src/pages/community/index.tsx +++ b/microsite/src/pages/community/index.tsx @@ -36,11 +36,6 @@ const Community = () => { label: 'Community newsletter', link: 'https://info.backstage.spotify.com/newsletter_subscribe', }, - { - content: 'Join the', - label: 'Twitter community', - link: 'https://twitter.com/i/communities/1494019781716062215', - }, ]; const officialInitiatives: ICollectionItem[] = [ @@ -58,45 +53,11 @@ const Community = () => { link: 'https://info.backstage.spotify.com/newsletter_subscribe', label: 'Subscribe', }, - { - title: 'Contributor Spotlight', - content: - "A recognition for valuable community work. Nominate contributing members for their efforts! We'll put them in the spotlight ❤️", - link: '/nominate', - label: 'Nominate now', - }, - ]; - - const communityInitiatives: ICollectionItem[] = [ - { - title: 'Open Mic Meetup', - content: ( - <> - A casual get together of Backstage users sharing their experiences and - helping each other. Hosted by{' '} - Roadie.io and{' '} - Frontside Software. - - ), - link: 'https://backstage-openmic.com/', - label: 'Learn more', - }, - { - title: 'Backstage Weekly Newsletter', - content: ( - <> - A weekly newsletter with news, updates and things community from your - friends at Roadie.io. - - ), - link: 'https://roadie.io/backstage-weekly/', - label: 'Learn more', - }, ]; const trainingNCertifications: ICollectionItem[] = [ { - title: 'Open Mic Meetup', + title: 'Introduction to Backstage: Developer Portals Made Easy (LFS142x)', content: 'This is a course produced and curated by the Linux Foundation. This course introduces you to Backstage and how to get started with the project.', link: 'https://training.linuxfoundation.org/training/introduction-to-backstage-developer-portals-made-easy-lfs142x/', @@ -204,30 +165,6 @@ const Community = () => { - - Community initiatives} - > - {communityInitiatives.map( - ({ title, content, link, label }, index) => ( - -

{content}

-
- ), - )} -
-
- { - return ( - -
- - Contributor Spotlight nomination}> - - - -
-
- ); -}; - -export default Nominate; diff --git a/microsite/src/pages/nominate/nominate.module.scss b/microsite/src/pages/nominate/nominate.module.scss deleted file mode 100644 index cdec103a8a..0000000000 --- a/microsite/src/pages/nominate/nominate.module.scss +++ /dev/null @@ -1,32 +0,0 @@ -.nominatePage { - font-size: 1.25rem; - - &, - & > section, - & > section > div { - flex: 1; - display: flex; - flex-direction: column; - } - - & > section > div > div { - flex: 1; - grid-template-rows: auto 1fr; - } - - h1 { - font-size: 54px; - line-height: 56px; - word-break: break-word; - } - - p { - text-align: justify; - } - - iframe { - width: 100%; - height: 100%; - min-height: 400px; - } -} From a950ed0c7d36524ae6c7a669c3b019d3d181ee63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Fri, 12 Jan 2024 13:12:12 +0000 Subject: [PATCH 13/39] added changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .changeset/thirty-dolls-admire.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/thirty-dolls-admire.md diff --git a/.changeset/thirty-dolls-admire.md b/.changeset/thirty-dolls-admire.md new file mode 100644 index 0000000000..2b39472f8d --- /dev/null +++ b/.changeset/thirty-dolls-admire.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-backend-module-github': minor +'@backstage/plugin-catalog-backend-module-ldap': minor +'@backstage/plugin-catalog-backend-module-msgraph': minor +--- + +Prevent Entity Providers from eliminating Users and Groups from the DB when the synchronisation fails From 1a37e86b9dfe33aa14308b0a685060ddc2e8c1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Fri, 12 Jan 2024 13:14:39 +0000 Subject: [PATCH 14/39] added break line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../src/providers/GithubMultiOrgEntityProvider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index 858095e766..1465857092 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -298,6 +298,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { } const allUsers = Array.from(allUsersMap.values()); + if (allUsers.length > 0 || allTeams.length > 0) { const { markCommitComplete } = markReadComplete({ allUsers, allTeams }); From 7647d8d870a6486ef7567eee722edba82e4b6881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 16 Jan 2024 14:26:14 +0000 Subject: [PATCH 15/39] removed if statements checking length, removed try catch preventing error from breaking execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../providers/GithubMultiOrgEntityProvider.ts | 102 ++++++++---------- .../src/providers/GithubOrgEntityProvider.ts | 30 +++--- .../src/processors/LdapOrgEntityProvider.ts | 22 ++-- .../MicrosoftGraphOrgEntityProvider.ts | 22 ++-- 4 files changed, 78 insertions(+), 98 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index 1465857092..8387a91e3a 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -248,75 +248,67 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { : await this.getAllOrgs(this.options.gitHubConfig); for (const org of orgsToProcess) { - try { - const { headers, type: tokenType } = - await this.options.githubCredentialsProvider.getCredentials({ - url: `${this.options.githubUrl}/${org}`, - }); - const client = graphql.defaults({ - baseUrl: this.options.gitHubConfig.apiBaseUrl, - headers, + const { headers, type: tokenType } = + await this.options.githubCredentialsProvider.getCredentials({ + url: `${this.options.githubUrl}/${org}`, }); + const client = graphql.defaults({ + baseUrl: this.options.gitHubConfig.apiBaseUrl, + headers, + }); - logger.info(`Reading GitHub users and teams for org: ${org}`); + logger.info(`Reading GitHub users and teams for org: ${org}`); - const { users } = await getOrganizationUsers( - client, - org, - tokenType, - this.options.userTransformer, - ); + const { users } = await getOrganizationUsers( + client, + org, + tokenType, + this.options.userTransformer, + ); - const { teams } = await getOrganizationTeams( - client, - org, - this.defaultMultiOrgTeamTransformer.bind(this), - ); + const { teams } = await getOrganizationTeams( + client, + org, + this.defaultMultiOrgTeamTransformer.bind(this), + ); - // Grab current users from `allUsersMap` if they already exist in our - // pending users so we can append to their group membership relations - const pendingUsers = users.map(u => { - const userRef = stringifyEntityRef(u); - if (!allUsersMap.has(userRef)) { - allUsersMap.set(userRef, u); - } - - return allUsersMap.get(userRef); - }); - - if (areGroupEntities(teams)) { - buildOrgHierarchy(teams); - if (areUserEntities(pendingUsers)) { - assignGroupsToUsers(pendingUsers, teams); - } + // Grab current users from `allUsersMap` if they already exist in our + // pending users so we can append to their group membership relations + const pendingUsers = users.map(u => { + const userRef = stringifyEntityRef(u); + if (!allUsersMap.has(userRef)) { + allUsersMap.set(userRef, u); } - allTeams.push(...teams); - } catch (e) { - logger.error(`Failed to read GitHub org data for ${org}: ${e}`); + return allUsersMap.get(userRef); + }); + + if (areGroupEntities(teams)) { + buildOrgHierarchy(teams); + if (areUserEntities(pendingUsers)) { + assignGroupsToUsers(pendingUsers, teams); + } } + + allTeams.push(...teams); } const allUsers = Array.from(allUsersMap.values()); - if (allUsers.length > 0 || allTeams.length > 0) { - const { markCommitComplete } = markReadComplete({ allUsers, allTeams }); + const { markCommitComplete } = markReadComplete({ allUsers, allTeams }); - await this.connection.applyMutation({ - type: 'full', - entities: [...allUsers, ...allTeams].map(entity => ({ - locationKey: `github-multi-org-provider:${this.options.id}`, - entity: withLocations( - `https://${this.options.gitHubConfig.host}`, - entity, - ), - })), - }); + await this.connection.applyMutation({ + type: 'full', + entities: [...allUsers, ...allTeams].map(entity => ({ + locationKey: `github-multi-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + entity, + ), + })), + }); - markCommitComplete(); - } else { - logger.info('No users or teams to process'); - } + markCommitComplete(); } private supportsEventTopics(): string[] { diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index a3ff980fd6..0fe61aa229 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -229,25 +229,21 @@ export class GithubOrgEntityProvider } } - if (users.length > 0 || teams.length > 0) { - const { markCommitComplete } = markReadComplete({ users, teams }); + const { markCommitComplete } = markReadComplete({ users, teams }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...teams].map(entity => ({ - locationKey: `github-org-provider:${this.options.id}`, - entity: withLocations( - `https://${this.options.gitHubConfig.host}`, - org, - entity, - ), - })), - }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...teams].map(entity => ({ + locationKey: `github-org-provider:${this.options.id}`, + entity: withLocations( + `https://${this.options.gitHubConfig.host}`, + org, + entity, + ), + })), + }); - markCommitComplete(); - } else { - logger.info('No users or teams to process'); - } + markCommitComplete(); } /** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.onEvent} */ diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts index ced28face8..1a35852a8d 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts @@ -192,21 +192,17 @@ export class LdapOrgEntityProvider implements EntityProvider { logger, }, ); - if (users.length > 0 || groups.length > 0) { - const { markCommitComplete } = markReadComplete({ users, groups }); + const { markCommitComplete } = markReadComplete({ users, groups }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...groups].map(entity => ({ - locationKey: `ldap-org-provider:${this.options.id}`, - entity: withLocations(this.options.id, entity), - })), - }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...groups].map(entity => ({ + locationKey: `ldap-org-provider:${this.options.id}`, + entity: withLocations(this.options.id, entity), + })), + }); - markCommitComplete(); - } else { - logger.info('No users or teams to process'); - } + markCommitComplete(); } private schedule(schedule: LdapOrgEntityProviderOptions['schedule']) { diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 3a3d5c29c4..7c4c41996b 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -323,21 +323,17 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { logger: logger, }, ); - if (users.length > 0 || groups.length > 0) { - const { markCommitComplete } = markReadComplete({ users, groups }); + const { markCommitComplete } = markReadComplete({ users, groups }); - await this.connection.applyMutation({ - type: 'full', - entities: [...users, ...groups].map(entity => ({ - locationKey: `msgraph-org-provider:${this.options.id}`, - entity: withLocations(this.options.id, entity), - })), - }); + await this.connection.applyMutation({ + type: 'full', + entities: [...users, ...groups].map(entity => ({ + locationKey: `msgraph-org-provider:${this.options.id}`, + entity: withLocations(this.options.id, entity), + })), + }); - markCommitComplete(); - } else { - logger.info('No users or teams to process'); - } + markCommitComplete(); } private schedule(taskRunner: TaskRunner) { From 6dcd52322735b89de3cd0d4f67bb44c094843988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 16 Jan 2024 14:27:55 +0000 Subject: [PATCH 16/39] updated changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .changeset/thirty-dolls-admire.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changeset/thirty-dolls-admire.md b/.changeset/thirty-dolls-admire.md index 2b39472f8d..1011508a98 100644 --- a/.changeset/thirty-dolls-admire.md +++ b/.changeset/thirty-dolls-admire.md @@ -1,7 +1,5 @@ --- '@backstage/plugin-catalog-backend-module-github': minor -'@backstage/plugin-catalog-backend-module-ldap': minor -'@backstage/plugin-catalog-backend-module-msgraph': minor --- Prevent Entity Providers from eliminating Users and Groups from the DB when the synchronisation fails From eb896509a23d09f65e31def9a193ffbf5b690ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 16 Jan 2024 14:28:54 +0000 Subject: [PATCH 17/39] added breaking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../src/processors/LdapOrgEntityProvider.ts | 1 + .../src/processors/MicrosoftGraphOrgEntityProvider.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts index 1a35852a8d..d4f19d0522 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts @@ -192,6 +192,7 @@ export class LdapOrgEntityProvider implements EntityProvider { logger, }, ); + const { markCommitComplete } = markReadComplete({ users, groups }); await this.connection.applyMutation({ diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 7c4c41996b..160696e2e7 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -323,6 +323,7 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { logger: logger, }, ); + const { markCommitComplete } = markReadComplete({ users, groups }); await this.connection.applyMutation({ From 1c39e619a78cf1c1ee84705bc034d6373425335f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 16 Jan 2024 16:08:49 +0000 Subject: [PATCH 18/39] added test to assure mutation is not applied when request fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../providers/GithubOrgEntityProvider.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index c054f26522..1c563b01b8 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -171,6 +171,53 @@ describe('GithubOrgEntityProvider', () => { type: 'full', }); }); + + // New test case for handling request failure + it('should not apply mutation if a request fails', async () => { + const mockClient = jest.fn(); + + // Simulate a request failure + mockClient.mockRejectedValue(new Error('Network error')); + + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + const logger = getVoidLogger(); + + const gitHubConfig: GithubIntegrationConfig = { + host: 'https://github.com', + }; + + const mockGetCredentials = jest.fn().mockReturnValue({ + headers: { token: 'blah' }, + type: 'app', + }); + + const githubCredentialsProvider: GithubCredentialsProvider = { + getCredentials: mockGetCredentials, + }; + + const entityProvider = new GithubOrgEntityProvider({ + id: 'my-id', + githubCredentialsProvider, + orgUrl: 'https://github.com/backstage', + gitHubConfig, + logger, + }); + + entityProvider.connect(entityProviderConnection); + + try { + await entityProvider.read(); + } catch (e) { + // Failed successfuly! + } + expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); + }); }); describe('withLocations', () => { From a16c772dd896f94d3645508e0d07334eadec0b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Tue, 16 Jan 2024 16:15:10 +0000 Subject: [PATCH 19/39] removed redundancies in the tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../providers/GithubOrgEntityProvider.test.ts | 127 +++++++----------- 1 file changed, 46 insertions(+), 81 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index 1c563b01b8..5aa01c1acf 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -30,13 +30,51 @@ jest.mock('@octokit/graphql'); describe('GithubOrgEntityProvider', () => { describe('read', () => { + let mockClient; + let entityProviderConnection; + let entityProvider; + + const setupMocks = response => { + mockClient = jest.fn().mockImplementation(response); + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + }; + + beforeEach(() => { + entityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + const logger = getVoidLogger(); + const gitHubConfig = { + host: 'https://github.com', + }; + + const mockGetCredentials = jest.fn().mockReturnValue({ + headers: { token: 'blah' }, + type: 'app', + }); + + const githubCredentialsProvider = { + getCredentials: mockGetCredentials, + }; + + entityProvider = new GithubOrgEntityProvider({ + id: 'my-id', + githubCredentialsProvider, + orgUrl: 'https://github.com/backstage', + gitHubConfig, + logger, + }); + + entityProvider.connect(entityProviderConnection); + }); + afterEach(() => jest.resetAllMocks()); it('should read org data and apply mutation', async () => { - const mockClient = jest.fn(); - - mockClient - .mockResolvedValueOnce({ + setupMocks(() => + Promise.resolve({ organization: { membersWithRole: { pageInfo: { hasNextPage: false }, @@ -50,10 +88,6 @@ describe('GithubOrgEntityProvider', () => { }, ], }, - }, - }) - .mockResolvedValueOnce({ - organization: { teams: { pageInfo: { hasNextPage: false }, nodes: [ @@ -76,38 +110,8 @@ describe('GithubOrgEntityProvider', () => { ], }, }, - }); - - (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - - const entityProviderConnection: EntityProviderConnection = { - applyMutation: jest.fn(), - refresh: jest.fn(), - }; - - const logger = getVoidLogger(); - const gitHubConfig: GithubIntegrationConfig = { - host: 'https://github.com', - }; - - const mockGetCredentials = jest.fn().mockReturnValue({ - headers: { token: 'blah' }, - type: 'app', - }); - - const githubCredentialsProvider: GithubCredentialsProvider = { - getCredentials: mockGetCredentials, - }; - - const entityProvider = new GithubOrgEntityProvider({ - id: 'my-id', - githubCredentialsProvider, - orgUrl: 'https://github.com/backstage', - gitHubConfig, - logger, - }); - - entityProvider.connect(entityProviderConnection); + }), + ); await entityProvider.read(); @@ -172,50 +176,11 @@ describe('GithubOrgEntityProvider', () => { }); }); - // New test case for handling request failure it('should not apply mutation if a request fails', async () => { - const mockClient = jest.fn(); + setupMocks(() => Promise.reject(new Error('Network error'))); - // Simulate a request failure - mockClient.mockRejectedValue(new Error('Network error')); + await expect(entityProvider.read()).rejects.toThrow('Network error'); - (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - - const entityProviderConnection: EntityProviderConnection = { - applyMutation: jest.fn(), - refresh: jest.fn(), - }; - - const logger = getVoidLogger(); - - const gitHubConfig: GithubIntegrationConfig = { - host: 'https://github.com', - }; - - const mockGetCredentials = jest.fn().mockReturnValue({ - headers: { token: 'blah' }, - type: 'app', - }); - - const githubCredentialsProvider: GithubCredentialsProvider = { - getCredentials: mockGetCredentials, - }; - - const entityProvider = new GithubOrgEntityProvider({ - id: 'my-id', - githubCredentialsProvider, - orgUrl: 'https://github.com/backstage', - gitHubConfig, - logger, - }); - - entityProvider.connect(entityProviderConnection); - - try { - await entityProvider.read(); - } catch (e) { - // Failed successfuly! - } expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); }); }); From 7866861572955369ebbc1c04ebf4af30305392c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Thu, 18 Jan 2024 11:54:25 +0000 Subject: [PATCH 20/39] Added tests to GithubMultiOrgEntityProvider, reduced redundancies in the mocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../GithubMultiOrgEntityProvider.test.ts | 102 +++++++++--------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts index 504e5772dd..51ff514ce9 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts @@ -43,11 +43,50 @@ jest.mock('@backstage/integration', () => ({ describe('GithubMultiOrgEntityProvider', () => { describe('read', () => { + let mockClient; + let entityProviderConnection; + let logger; + let gitHubConfig; + let mockGetCredentials; + let entityProvider; + + beforeEach(() => { + mockClient = jest.fn(); + (graphql.defaults as jest.Mock).mockReturnValue(mockClient); + + entityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + logger = getVoidLogger(); + + gitHubConfig = { host: 'github.com' }; + + mockGetCredentials = jest.fn().mockReturnValue({ + headers: { token: 'blah' }, + type: 'app', + }); + + const githubCredentialsProvider: GithubCredentialsProvider = { + getCredentials: mockGetCredentials, + }; + + entityProvider = new GithubMultiOrgEntityProvider({ + id: 'my-id', + gitHubConfig, + githubCredentialsProvider, + githubUrl: 'https://github.com', + logger, + orgs: ['orgA', 'orgB'], // only include for tests that require it + }); + + entityProvider.connect(entityProviderConnection); + }); + afterEach(() => jest.resetAllMocks()); it('should read specified orgs', async () => { - const mockClient = jest.fn(); - mockClient .mockResolvedValueOnce({ organization: { @@ -155,36 +194,6 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - const entityProviderConnection: EntityProviderConnection = { - applyMutation: jest.fn(), - refresh: jest.fn(), - }; - - const logger = getVoidLogger(); - const gitHubConfig: GithubIntegrationConfig = { - host: 'github.com', - }; - - const mockGetCredentials = jest.fn().mockReturnValue({ - headers: { token: 'blah' }, - type: 'app', - }); - - const githubCredentialsProvider: GithubCredentialsProvider = { - getCredentials: mockGetCredentials, - }; - - const entityProvider = new GithubMultiOrgEntityProvider({ - id: 'my-id', - gitHubConfig, - githubCredentialsProvider, - githubUrl: 'https://github.com', - logger, - orgs: ['orgA', 'orgB'], - }); - - entityProvider.connect(entityProviderConnection); - await entityProvider.read(); expect(mockGetCredentials).toHaveBeenCalledWith({ @@ -353,8 +362,6 @@ describe('GithubMultiOrgEntityProvider', () => { }, ]); - const mockClient = jest.fn(); - mockClient .mockResolvedValueOnce({ organization: { @@ -462,26 +469,11 @@ describe('GithubMultiOrgEntityProvider', () => { (graphql.defaults as jest.Mock).mockReturnValue(mockClient); - const entityProviderConnection: EntityProviderConnection = { - applyMutation: jest.fn(), - refresh: jest.fn(), - }; - - const logger = getVoidLogger(); - const gitHubConfig: GithubIntegrationConfig = { - host: 'github.com', - }; - - const mockGetCredentials = jest.fn().mockReturnValue({ - headers: { token: 'blah' }, - type: 'app', - }); - const githubCredentialsProvider: GithubCredentialsProvider = { getCredentials: mockGetCredentials, }; - const entityProvider = new GithubMultiOrgEntityProvider({ + entityProvider = new GithubMultiOrgEntityProvider({ id: 'my-id', gitHubConfig, githubCredentialsProvider, @@ -642,6 +634,16 @@ describe('GithubMultiOrgEntityProvider', () => { type: 'full', }); }); + + it('should not call applyMutation if an error is thrown', async () => { + mockClient.mockImplementationOnce(() => { + throw new Error('Network Error'); + }); + + await expect(entityProvider.read()).rejects.toThrow('Network Error'); + + expect(entityProviderConnection.applyMutation).not.toHaveBeenCalled(); + }); }); describe('withLocations', () => { From 1d5f379be5b9d3991fad402579f3409c71e72149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Wed, 24 Jan 2024 15:23:53 +0000 Subject: [PATCH 21/39] Added TS definitions to tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../providers/GithubMultiOrgEntityProvider.test.ts | 13 +++++++------ .../src/providers/GithubOrgEntityProvider.test.ts | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts index 51ff514ce9..9b0d9ab7c8 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts @@ -28,6 +28,7 @@ import { GithubMultiOrgEntityProvider, withLocations, } from './GithubMultiOrgEntityProvider'; +import { Logger } from 'winston'; jest.mock('@octokit/graphql'); @@ -43,12 +44,12 @@ jest.mock('@backstage/integration', () => ({ describe('GithubMultiOrgEntityProvider', () => { describe('read', () => { - let mockClient; - let entityProviderConnection; - let logger; - let gitHubConfig; - let mockGetCredentials; - let entityProvider; + let mockClient: jest.Mock; + let entityProviderConnection: EntityProviderConnection; + let logger: Logger; + let gitHubConfig: { host: string }; + let mockGetCredentials: jest.Mock; + let entityProvider: GithubMultiOrgEntityProvider; beforeEach(() => { mockClient = jest.fn(); diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index 5aa01c1acf..486ff47f1c 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -31,10 +31,10 @@ jest.mock('@octokit/graphql'); describe('GithubOrgEntityProvider', () => { describe('read', () => { let mockClient; - let entityProviderConnection; - let entityProvider; + let entityProviderConnection: EntityProviderConnection; + let entityProvider: GithubOrgEntityProvider; - const setupMocks = response => { + const setupMocks = (response: ((...args: any) => any) | undefined) => { mockClient = jest.fn().mockImplementation(response); (graphql.defaults as jest.Mock).mockReturnValue(mockClient); }; From 4134450da3f8383ef9fae524d764c71390b538bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Costa?= Date: Wed, 24 Jan 2024 15:26:38 +0000 Subject: [PATCH 22/39] removed unecessary import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Costa --- .../src/providers/GithubMultiOrgEntityProvider.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts index 9b0d9ab7c8..78c12d92cf 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.test.ts @@ -17,10 +17,7 @@ import { getVoidLogger } from '@backstage/backend-common'; import { GroupEntity, UserEntity } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; -import { - GithubCredentialsProvider, - GithubIntegrationConfig, -} from '@backstage/integration'; +import { GithubCredentialsProvider } from '@backstage/integration'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { EventSubscriber } from '@backstage/plugin-events-node'; import { graphql } from '@octokit/graphql'; From 1ba7f03f4932fa80923a0eecdf9e70de16b9bddc Mon Sep 17 00:00:00 2001 From: Abel Rodriguez Date: Wed, 24 Jan 2024 14:14:06 -0600 Subject: [PATCH 23/39] add forceFork input to createPullRequest Signed-off-by: Abel Rodriguez --- .../src/actions/githubPullRequest.test.ts | 53 +++++++++++++++++++ .../src/actions/githubPullRequest.ts | 8 +++ 2 files changed, 61 insertions(+) diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts index c5b1038a01..3fe4f62e04 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts @@ -687,4 +687,57 @@ describe('createPublishGithubPullRequestAction', () => { }); }); }); + + describe('with force fork', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + forceFork: true, + }; + + mockDir.setContent({ + [workspacePath]: { 'file.txt': 'Hello there!' }, + }); + + ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + }); + + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + forceFork: true, + }); + }); + }); }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts index 2d8dc7ce8d..6a5c939905 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts @@ -135,6 +135,7 @@ export const createPublishGithubPullRequestAction = ( teamReviewers?: string[]; commitMessage?: string; update?: boolean; + forceFork?: boolean; }>({ id: 'publish:github:pull-request', examples, @@ -217,6 +218,11 @@ export const createPublishGithubPullRequestAction = ( title: 'Update', description: 'Update pull request if already exists', }, + forceFork: { + type: 'boolean', + title: 'Force Fork', + description: 'Create pull request from a fork', + }, }, }, output: { @@ -255,6 +261,7 @@ export const createPublishGithubPullRequestAction = ( teamReviewers, commitMessage, update, + forceFork, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -327,6 +334,7 @@ export const createPublishGithubPullRequestAction = ( head: branchName, draft, update, + forceFork, }; if (targetBranchName) { createOptions.base = targetBranchName; From fd5eb1c176dd855882dc997231d41e1ab2d753d3 Mon Sep 17 00:00:00 2001 From: Abel Rodriguez Date: Wed, 24 Jan 2024 14:32:23 -0600 Subject: [PATCH 24/39] add changeset Signed-off-by: Abel Rodriguez --- .changeset/calm-items-double.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/calm-items-double.md diff --git a/.changeset/calm-items-double.md b/.changeset/calm-items-double.md new file mode 100644 index 0000000000..c16f989845 --- /dev/null +++ b/.changeset/calm-items-double.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Allow to force the creation of a pull request from a forked repository From 838b74fe6a8c7c9a0bbdc6dbac1fb9beeabc84b4 Mon Sep 17 00:00:00 2001 From: Abel Rodriguez Date: Wed, 24 Jan 2024 14:41:43 -0600 Subject: [PATCH 25/39] update api reports Signed-off-by: Abel Rodriguez --- plugins/scaffolder-backend-module-github/api-report.md | 1 + plugins/scaffolder-backend/api-report.md | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/scaffolder-backend-module-github/api-report.md b/plugins/scaffolder-backend-module-github/api-report.md index 920be79d12..42aee02014 100644 --- a/plugins/scaffolder-backend-module-github/api-report.md +++ b/plugins/scaffolder-backend-module-github/api-report.md @@ -374,6 +374,7 @@ export const createPublishGithubPullRequestAction: ( teamReviewers?: string[] | undefined; commitMessage?: string | undefined; update?: boolean | undefined; + forceFork?: boolean | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index c928e5341e..b231267ede 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -266,6 +266,7 @@ export const createPublishGithubPullRequestAction: ( teamReviewers?: string[] | undefined; commitMessage?: string | undefined; update?: boolean | undefined; + forceFork?: boolean | undefined; }, JsonObject >; From 6f704931fb0aa4331c06d807a80430eb6a62a3d0 Mon Sep 17 00:00:00 2001 From: Abel Rodriguez Date: Wed, 24 Jan 2024 14:49:09 -0600 Subject: [PATCH 26/39] update changeset Signed-off-by: Abel Rodriguez --- .changeset/calm-items-double.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/calm-items-double.md b/.changeset/calm-items-double.md index c16f989845..8245fb06c4 100644 --- a/.changeset/calm-items-double.md +++ b/.changeset/calm-items-double.md @@ -1,4 +1,5 @@ --- +'@backstage/plugin-scaffolder-backend-module-github': minor '@backstage/plugin-scaffolder-backend': minor --- From 7052918391cc84d98d0c553682da96ffb2d88992 Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Thu, 25 Jan 2024 16:04:35 +0100 Subject: [PATCH 27/39] do not export the SecureTemplateOptions Signed-off-by: Kiss Miklos --- .../src/lib/templating/SecureTemplater.ts | 5 ++--- plugins/scaffolder-node/api-report.md | 6 ------ plugins/scaffolder-node/src/index.ts | 2 +- plugins/scaffolder-node/src/types.ts | 3 --- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index 8323b4aa98..607b1c6add 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -19,7 +19,6 @@ import { resolvePackagePath } from '@backstage/backend-common'; import { TemplateFilter as _TemplateFilter, TemplateGlobal as _TemplateGlobal, - NunjucksConfigs, } from '@backstage/plugin-scaffolder-node'; import fs from 'fs-extra'; import { JsonValue } from '@backstage/types'; @@ -105,14 +104,14 @@ export type TemplateFilter = _TemplateFilter; */ export type TemplateGlobal = _TemplateGlobal; -export interface SecureTemplaterOptions { +interface SecureTemplaterOptions { /* Enables jinja compatibility and the "jsonify" filter */ cookiecutterCompat?: boolean; /* Extra user-provided nunjucks filters */ templateFilters?: Record; /* Extra user-provided nunjucks globals */ templateGlobals?: Record; - nunjucksConfigs?: NunjucksConfigs; + nunjucksConfigs?: { trimBlocks?: boolean; lstripBlocks?: boolean }; } export type SecureTemplateRenderer = ( diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 877f979656..6bb1534679 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -163,12 +163,6 @@ export function initRepoAndPush(input: { commitHash: string; }>; -// @public (undocumented) -export type NunjucksConfigs = { - trimBlocks?: boolean; - lstripBlocks?: boolean; -}; - // @public (undocumented) export const parseRepoUrl: ( repoUrl: string, diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index a049ffd9b7..0ec492dd32 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -23,4 +23,4 @@ export * from './actions'; export * from './tasks'; export * from './files'; -export type { TemplateFilter, TemplateGlobal, NunjucksConfigs } from './types'; +export type { TemplateFilter, TemplateGlobal } from './types'; diff --git a/plugins/scaffolder-node/src/types.ts b/plugins/scaffolder-node/src/types.ts index 892570af80..bae106c84e 100644 --- a/plugins/scaffolder-node/src/types.ts +++ b/plugins/scaffolder-node/src/types.ts @@ -23,6 +23,3 @@ export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; export type TemplateGlobal = | ((...args: JsonValue[]) => JsonValue | undefined) | JsonValue; - -/** @public */ -export type NunjucksConfigs = { trimBlocks?: boolean; lstripBlocks?: boolean }; From 75b51a87b6b392616a28cde39fb30de0918b5e1f Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 25 Jan 2024 19:32:54 +0100 Subject: [PATCH 28/39] return interface instead of boolean Signed-off-by: secustor --- .../src/reading/GithubUrlReader.ts | 2 +- packages/integration/api-report.md | 8 +++++++- .../src/github/GithubIntegration.test.ts | 6 ++++-- .../src/github/GithubIntegration.ts | 19 ++++++++++++------- packages/integration/src/index.ts | 1 + packages/integration/src/types.ts | 9 +++++++++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index edd5269649..462821a20f 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -333,7 +333,7 @@ export class GithubUrlReader implements UrlReader { // GitHub returns a 403 response with a couple of headers indicating rate // limit status. See more in the GitHub docs: // https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting - if (this.integration.isRateLimited(response)) { + if (this.integration.parseRateLimitInfo(response).isRateLimited) { message += ' (rate limit exceeded)'; } diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index f830437cf1..07b4f7d012 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -568,7 +568,7 @@ export class GithubIntegration implements ScmIntegration { // (undocumented) static factory: ScmIntegrationsFactory; // (undocumented) - isRateLimited(response: ConsumedResponse): boolean; + parseRateLimitInfo(response: ConsumedResponse): RateLimitInfo; // (undocumented) resolveEditUrl(url: string): string; // (undocumented) @@ -697,6 +697,12 @@ export type PersonalAccessTokenCredential = AzureCredentialBase & { personalAccessToken: string; }; +// @public +export interface RateLimitInfo { + // (undocumented) + isRateLimited: boolean; +} + // @public export function readAwsS3IntegrationConfig( config: Config, diff --git a/packages/integration/src/github/GithubIntegration.test.ts b/packages/integration/src/github/GithubIntegration.test.ts index 36c802da52..70f1c4a16c 100644 --- a/packages/integration/src/github/GithubIntegration.test.ts +++ b/packages/integration/src/github/GithubIntegration.test.ts @@ -95,11 +95,13 @@ describe('GithubIntegration', () => { const headers = new Headers({ 'x-ratelimit-remaining': ratelimitRemaining, }); - const result = integration.isRateLimited({ + const result = integration.parseRateLimitInfo({ status, headers, } as Response); - expect(expected).toBe(result); + expect(result).toMatchObject({ + isRateLimited: expected, + }); }, ); }); diff --git a/packages/integration/src/github/GithubIntegration.ts b/packages/integration/src/github/GithubIntegration.ts index 7a6ac5675d..880a2bed21 100644 --- a/packages/integration/src/github/GithubIntegration.ts +++ b/packages/integration/src/github/GithubIntegration.ts @@ -15,7 +15,11 @@ */ import { basicIntegrations, defaultScmResolveUrl } from '../helpers'; -import { ScmIntegration, ScmIntegrationsFactory } from '../types'; +import { + RateLimitInfo, + ScmIntegration, + ScmIntegrationsFactory, +} from '../types'; import { GithubIntegrationConfig, readGithubIntegrationConfigs, @@ -67,12 +71,13 @@ export class GithubIntegration implements ScmIntegration { return replaceGithubUrlType(url, 'edit'); } - isRateLimited(response: ConsumedResponse): boolean { - return ( - response.status === 429 || - (response.status === 403 && - response.headers.get('x-ratelimit-remaining') === '0') - ); + parseRateLimitInfo(response: ConsumedResponse): RateLimitInfo { + return { + isRateLimited: + response.status === 429 || + (response.status === 403 && + response.headers.get('x-ratelimit-remaining') === '0'), + }; } } diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index 5738da3da0..700d7dc67c 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -37,5 +37,6 @@ export type { ScmIntegration, ScmIntegrationsFactory, ScmIntegrationsGroup, + RateLimitInfo, } from './types'; export type { ScmIntegrationRegistry } from './registry'; diff --git a/packages/integration/src/types.ts b/packages/integration/src/types.ts index cb4b78eb7c..0f0fca942e 100644 --- a/packages/integration/src/types.ts +++ b/packages/integration/src/types.ts @@ -108,3 +108,12 @@ export interface ScmIntegrationsGroup { export type ScmIntegrationsFactory = (options: { config: Config; }) => ScmIntegrationsGroup; + +/** + * Encapsulates information about the RateLimit state + * + * @public + */ +export interface RateLimitInfo { + isRateLimited: boolean; +} From 94771339d8cb346e58618a134303438ab67e402e Mon Sep 17 00:00:00 2001 From: Jonathan Nagayoshi Date: Thu, 25 Jan 2024 21:24:42 +0000 Subject: [PATCH 29/39] fix(catalog-backend-module-github): decreases number of teams fetched in graphql at a time Signed-off-by: Jonathan Nagayoshi --- .changeset/nice-carrots-dream.md | 5 +++++ plugins/catalog-backend-module-github/src/lib/github.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/nice-carrots-dream.md diff --git a/.changeset/nice-carrots-dream.md b/.changeset/nice-carrots-dream.md new file mode 100644 index 0000000000..3b57dfd9e0 --- /dev/null +++ b/.changeset/nice-carrots-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Decreased number of teams fetched by GraphQL Query responsible for fetching Teams and Members in organization, due to timeouts when running against big organizations diff --git a/plugins/catalog-backend-module-github/src/lib/github.ts b/plugins/catalog-backend-module-github/src/lib/github.ts index cf6b400443..eff52ea98d 100644 --- a/plugins/catalog-backend-module-github/src/lib/github.ts +++ b/plugins/catalog-backend-module-github/src/lib/github.ts @@ -193,7 +193,7 @@ export async function getOrganizationTeams( const query = ` query teams($org: String!, $cursor: String) { organization(login: $org) { - teams(first: 100, after: $cursor) { + teams(first: 50, after: $cursor) { pageInfo { hasNextPage, endCursor } nodes { slug From 701ace47b98a2fa53813d5c8bb21b8c685b1a007 Mon Sep 17 00:00:00 2001 From: Abel Rodriguez Date: Thu, 25 Jan 2024 15:14:28 -0600 Subject: [PATCH 30/39] remove unexpected changeset Signed-off-by: Abel Rodriguez --- .changeset/calm-items-double.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/calm-items-double.md b/.changeset/calm-items-double.md index 8245fb06c4..3db8ad57d9 100644 --- a/.changeset/calm-items-double.md +++ b/.changeset/calm-items-double.md @@ -1,6 +1,5 @@ --- '@backstage/plugin-scaffolder-backend-module-github': minor -'@backstage/plugin-scaffolder-backend': minor --- Allow to force the creation of a pull request from a forked repository From e93bc4fc130c4634a09e2d30652d8ddaad59700f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 26 Jan 2024 10:12:50 +0100 Subject: [PATCH 31/39] eslint-plugin: fix tests not restoring cwd Signed-off-by: Patrik Oldsberg --- .../eslint-plugin/src/no-forbidden-package-imports.test.ts | 5 +++++ .../eslint-plugin/src/no-relative-monorepo-imports.test.ts | 5 +++++ packages/eslint-plugin/src/no-undeclared-imports.test.ts | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts index 349b71319f..c46f82f4fc 100644 --- a/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts +++ b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts @@ -25,6 +25,11 @@ const ERR = (name: string, path: string) => ({ message: `${name} does not export ${path}`, }); +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); process.chdir(FIXTURE); const ruleTester = new RuleTester({ diff --git a/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts index 6888d258cc..31e1674b76 100644 --- a/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts +++ b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts @@ -28,6 +28,11 @@ const ERR_FORBIDDEN = (newImp: string) => ({ message: `Relative imports of monorepo packages are forbidden, use '${newImp}' instead`, }); +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); process.chdir(FIXTURE); const ruleTester = new RuleTester({ diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts index e92d422057..29e2c3a87c 100644 --- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -53,6 +53,11 @@ const ERR_SWITCH_BACK = () => ({ message: 'Switch back to import declaration', }); +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); process.chdir(FIXTURE); const ruleTester = new RuleTester({ From 1e61ad374a58b85ffb295817fa66fa2f4a3c0683 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 26 Jan 2024 13:13:45 +0100 Subject: [PATCH 32/39] frontend-plugin-api: no longer wrap component extension components in an ExtensionBoundary Signed-off-by: Patrik Oldsberg --- .changeset/strong-lobsters-hide.md | 5 +++ .../extensions/createComponentExtension.tsx | 34 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 .changeset/strong-lobsters-hide.md diff --git a/.changeset/strong-lobsters-hide.md b/.changeset/strong-lobsters-hide.md new file mode 100644 index 0000000000..7b51355eb7 --- /dev/null +++ b/.changeset/strong-lobsters-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +App component extensions are no longer wrapped in an `ExtensionBoundary`, allowing them to inherit the outer context instead. diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx index 2efa106750..a1231d8353 100644 --- a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { lazy, ComponentType } from 'react'; +import { lazy, ComponentType } from 'react'; import { AnyExtensionInputMap, ResolvedExtensionInputs, @@ -23,7 +23,7 @@ import { } from '../wiring'; import { Expand } from '../types'; import { PortableSchema } from '../schema'; -import { ExtensionBoundary, ComponentRef } from '../components'; +import { ComponentRef } from '../components'; /** @public */ export function createComponentExtension< @@ -61,28 +61,26 @@ export function createComponentExtension< output: { component: createComponentExtension.componentDataRef, }, - factory({ config, inputs, node }) { - let ExtensionComponent: ComponentType; - + factory({ config, inputs }) { if ('sync' in options.loader) { - ExtensionComponent = options.loader.sync({ config, inputs }); - } else { - const lazyLoader = options.loader.lazy; - ExtensionComponent = lazy(() => - lazyLoader({ config, inputs }).then(Component => ({ - default: Component, - })), - ) as unknown as ComponentType; + return { + component: { + ref: options.ref, + impl: options.loader.sync({ config, inputs }) as ComponentType, + }, + }; } + const lazyLoader = options.loader.lazy; + const ExtensionComponent = lazy(() => + lazyLoader({ config, inputs }).then(Component => ({ + default: Component, + })), + ) as unknown as ComponentType; return { component: { ref: options.ref, - impl: props => ( - - - - ), + impl: ExtensionComponent, }, }; }, From fb9b5e7bec0ab015fb0b3072c0968da19b54606a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 26 Jan 2024 13:14:38 +0100 Subject: [PATCH 33/39] frontend-app-api: key components by id instead of ref + tests Signed-off-by: Patrik Oldsberg --- .changeset/smart-numbers-call.md | 5 + .../DefaultComponentsApi.test.tsx | 111 ++++++++++++++++++ ...mponentsApi.ts => DefaultComponentsApi.ts} | 28 ++++- .../implementations/ComponentsApi/index.ts | 2 +- .../src/tree/resolveAppNodeSpecs.ts | 17 ++- .../frontend-app-api/src/wiring/createApp.tsx | 16 +-- 6 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 .changeset/smart-numbers-call.md create mode 100644 packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx rename packages/frontend-app-api/src/apis/implementations/ComponentsApi/{ComponentsApi.ts => DefaultComponentsApi.ts} (58%) diff --git a/.changeset/smart-numbers-call.md b/.changeset/smart-numbers-call.md new file mode 100644 index 0000000000..24a2053259 --- /dev/null +++ b/.changeset/smart-numbers-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +The default `ComponentsApi` implementation now uses the `ComponentRef` ID as the component key, rather than the reference instance. This fixes a bug where duplicate installations of `@backstage/frontend-plugin-api` would break the app. diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx new file mode 100644 index 0000000000..7d62103727 --- /dev/null +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx @@ -0,0 +1,111 @@ +/* + * 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 { + coreExtensionData, + createComponentExtension, + createComponentRef, + createExtension, + createExtensionOverrides, +} from '@backstage/frontend-plugin-api'; +import { resolveAppNodeSpecs } from '../../../tree/resolveAppNodeSpecs'; +import { resolveAppTree } from '../../../tree/resolveAppTree'; +import { App } from '../../../extensions/App'; +import { DefaultComponentsApi } from './DefaultComponentsApi'; +import { render, screen } from '@testing-library/react'; +import { instantiateAppNodeTree } from '../../../tree/instantiateAppNodeTree'; + +const testRefA = createComponentRef({ id: 'test.a' }); +const testRefB1 = createComponentRef({ id: 'test.b' }); +const testRefB2 = createComponentRef({ id: 'test.b' }); + +const baseOverrides = createExtensionOverrides({ + extensions: [ + App, + createExtension({ + namespace: 'app', + name: 'root', + attachTo: { id: 'app', input: 'root' }, + output: { + element: coreExtensionData.reactElement, + }, + factory() { + return { + element:
root
, + }; + }, + }), + ], +}); + +describe('DefaultComponentsApi', () => { + it('should provide components', () => { + const tree = resolveAppTree( + 'app', + resolveAppNodeSpecs({ + features: [ + baseOverrides, + createExtensionOverrides({ + extensions: [ + createComponentExtension({ + ref: testRefA, + loader: { sync: () => () =>
test.a
}, + }), + ], + }), + ], + }), + ); + instantiateAppNodeTree(tree.root); + const api = DefaultComponentsApi.fromTree(tree); + + const ComponentA = api.getComponent(testRefA); + render(); + + expect(screen.getByText('test.a')).toBeInTheDocument(); + }); + + it('should key extension refs by ID', () => { + const tree = resolveAppTree( + 'app', + resolveAppNodeSpecs({ + features: [ + baseOverrides, + createExtensionOverrides({ + extensions: [ + createComponentExtension({ + ref: testRefB1, + loader: { sync: () => () =>
test.b
}, + }), + ], + }), + ], + }), + ); + instantiateAppNodeTree(tree.root); + const api = DefaultComponentsApi.fromTree(tree); + + const ComponentB1 = api.getComponent(testRefB1); + const ComponentB2 = api.getComponent(testRefB2); + + expect(ComponentB1).toBe(ComponentB2); + + render(); + + expect(screen.getByText('test.b')).toBeInTheDocument(); + }); +}); diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts similarity index 58% rename from packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts rename to packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts index 4d8bc47e24..572b6fc3c4 100644 --- a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts @@ -15,7 +15,12 @@ */ import { ComponentType } from 'react'; -import { ComponentRef, ComponentsApi } from '@backstage/frontend-plugin-api'; +import { + AppTree, + ComponentRef, + ComponentsApi, + createComponentExtension, +} from '@backstage/frontend-plugin-api'; /** * Implementation for the {@linkComponentApi} @@ -23,14 +28,29 @@ import { ComponentRef, ComponentsApi } from '@backstage/frontend-plugin-api'; * @internal */ export class DefaultComponentsApi implements ComponentsApi { - #components: Map, ComponentType>; + #components: Map>; - constructor(components: Map, any>) { + static fromTree(tree: AppTree) { + const componentEntries = tree.root.edges.attachments + .get('components') + ?.reduce((map, e) => { + const data = e.instance?.getData( + createComponentExtension.componentDataRef, + ); + if (data) { + map.set(data.ref.id, data.impl); + } + return map; + }, new Map()); + return new DefaultComponentsApi(componentEntries ?? new Map()); + } + + constructor(components: Map) { this.#components = components; } getComponent(ref: ComponentRef): ComponentType { - const impl = this.#components.get(ref); + const impl = this.#components.get(ref.id); if (!impl) { throw new Error(`No implementation found for component ref ${ref}`); } diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts index f04059c54f..18604f15de 100644 --- a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { DefaultComponentsApi } from './ComponentsApi'; +export { DefaultComponentsApi } from './DefaultComponentsApi'; diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 8fd9d44638..f056e7a49e 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -31,17 +31,22 @@ import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/res /** @internal */ export function resolveAppNodeSpecs(options: { - features: FrontendFeature[]; - builtinExtensions: Extension[]; - parameters: Array; + features?: FrontendFeature[]; + builtinExtensions?: Extension[]; + parameters?: Array; forbidden?: Set; }): AppNodeSpec[] { - const { builtinExtensions, parameters, forbidden = new Set() } = options; + const { + builtinExtensions = [], + parameters = [], + forbidden = new Set(), + features = [], + } = options; - const plugins = options.features.filter( + const plugins = features.filter( (f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin', ); - const overrides = options.features.filter( + const overrides = features.filter( (f): f is ExtensionOverrides => f.$$type === '@backstage/ExtensionOverrides', ); diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 635e36aace..591e1cbf25 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -19,11 +19,9 @@ import { ConfigReader } from '@backstage/config'; import { AppTree, appTreeApiRef, - ComponentRef, componentsApiRef, coreExtensionData, createApiExtension, - createComponentExtension, createThemeExtension, createTranslationExtension, FrontendFeature, @@ -373,22 +371,10 @@ function createApiHolder( factory: () => routeResolutionApi, }); - const componentsExtensions = - tree.root.edges.attachments - .get('components') - ?.map(e => e.instance?.getData(createComponentExtension.componentDataRef)) - .filter(x => !!x) ?? []; - - const componentsMap = componentsExtensions.reduce( - (components, component) => - component ? components.set(component.ref, component?.impl) : components, - new Map, any>(), - ); - factoryRegistry.register('static', { api: componentsApiRef, deps: {}, - factory: () => new DefaultComponentsApi(componentsMap), + factory: () => DefaultComponentsApi.fromTree(tree), }); factoryRegistry.register('static', { From 53e682b82356f21ad5cf44f39c0d18e71ee58748 Mon Sep 17 00:00:00 2001 From: Frank Showalter <842058+fshowalter@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:03:58 -0500 Subject: [PATCH 34/39] Update react18-migration.md Fix a typo on the type resolution instructions. Signed-off-by: Frank Showalter <842058+fshowalter@users.noreply.github.com> --- docs/tutorials/react18-migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/react18-migration.md b/docs/tutorials/react18-migration.md index 2d3b3a961a..c11bc83a7e 100644 --- a/docs/tutorials/react18-migration.md +++ b/docs/tutorials/react18-migration.md @@ -21,9 +21,9 @@ To switch a project to React 18, there are generally three changes that need to // highlight-remove-next-line "@types/react": "^17", // highlight-remove-next-line - "@types/react": "^17", + "@types/react-dom": "^17", // highlight-add-next-line - "@types/react-dom": "^18", + "@types/react": "^18", // highlight-add-next-line "@types/react-dom": "^18", }, From b58673ef61abf7061b2a930670b075ab2acf05e4 Mon Sep 17 00:00:00 2001 From: Kamil Markow Date: Fri, 26 Jan 2024 08:46:02 -0500 Subject: [PATCH 35/39] Upgrade jest Signed-off-by: Kamil Markow --- .changeset/green-flies-draw.md | 5 +++++ packages/cli/package.json | 4 ++-- yarn.lock | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/green-flies-draw.md diff --git a/.changeset/green-flies-draw.md b/.changeset/green-flies-draw.md new file mode 100644 index 0000000000..6b967feceb --- /dev/null +++ b/.changeset/green-flies-draw.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Upgrade jest diff --git a/packages/cli/package.json b/packages/cli/package.json index 5ca7d675d3..12c00565d2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -62,7 +62,7 @@ "@swc/core": "^1.3.46", "@swc/helpers": "^0.5.0", "@swc/jest": "^0.2.22", - "@types/jest": "^29.0.0", + "@types/jest": "^29.5.11", "@types/webpack-env": "^1.15.2", "@typescript-eslint/eslint-plugin": "^6.12.0", "@typescript-eslint/parser": "^6.7.2", @@ -100,7 +100,7 @@ "handlebars": "^4.7.3", "html-webpack-plugin": "^5.3.1", "inquirer": "^8.2.0", - "jest": "^29.0.2", + "jest": "^29.7.0", "jest-css-modules": "^2.1.0", "jest-environment-jsdom": "^29.0.2", "jest-runtime": "^29.0.2", diff --git a/yarn.lock b/yarn.lock index 23bb660897..e9686c3676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3586,7 +3586,7 @@ __metadata: "@types/fs-extra": ^9.0.1 "@types/http-proxy": ^1.17.4 "@types/inquirer": ^8.1.3 - "@types/jest": ^29.0.0 + "@types/jest": ^29.5.11 "@types/minimatch": ^5.0.0 "@types/node": ^18.17.8 "@types/npm-packlist": ^3.0.0 @@ -3635,7 +3635,7 @@ __metadata: handlebars: ^4.7.3 html-webpack-plugin: ^5.3.1 inquirer: ^8.2.0 - jest: ^29.0.2 + jest: ^29.7.0 jest-css-modules: ^2.1.0 jest-environment-jsdom: ^29.0.2 jest-runtime: ^29.0.2 @@ -18213,7 +18213,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:^29.0.0": +"@types/jest@npm:*, @types/jest@npm:^29.0.0, @types/jest@npm:^29.5.11": version: 29.5.11 resolution: "@types/jest@npm:29.5.11" dependencies: @@ -31347,7 +31347,7 @@ __metadata: languageName: node linkType: hard -"jest@npm:^29.0.2": +"jest@npm:^29.7.0": version: 29.7.0 resolution: "jest@npm:29.7.0" dependencies: From 3dc36fae11dfef0566d0d38444e2da51ee37e077 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:25:58 +0000 Subject: [PATCH 36/39] chore(deps): update dependency esbuild to v0.19.12 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 190 +++++++++++++++++++++++++++--------------------------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5a15adff92..65dacde577 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10470,9 +10470,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/aix-ppc64@npm:0.19.11" +"@esbuild/aix-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/aix-ppc64@npm:0.19.12" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -10491,9 +10491,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-arm64@npm:0.19.11" +"@esbuild/android-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm64@npm:0.19.12" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -10512,9 +10512,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-arm@npm:0.19.11" +"@esbuild/android-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm@npm:0.19.12" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -10533,9 +10533,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-x64@npm:0.19.11" +"@esbuild/android-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-x64@npm:0.19.12" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -10554,9 +10554,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/darwin-arm64@npm:0.19.11" +"@esbuild/darwin-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-arm64@npm:0.19.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -10575,9 +10575,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/darwin-x64@npm:0.19.11" +"@esbuild/darwin-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-x64@npm:0.19.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -10596,9 +10596,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/freebsd-arm64@npm:0.19.11" +"@esbuild/freebsd-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-arm64@npm:0.19.12" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -10617,9 +10617,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/freebsd-x64@npm:0.19.11" +"@esbuild/freebsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-x64@npm:0.19.12" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -10638,9 +10638,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-arm64@npm:0.19.11" +"@esbuild/linux-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm64@npm:0.19.12" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -10659,9 +10659,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-arm@npm:0.19.11" +"@esbuild/linux-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm@npm:0.19.12" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -10680,9 +10680,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-ia32@npm:0.19.11" +"@esbuild/linux-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ia32@npm:0.19.12" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -10701,9 +10701,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-loong64@npm:0.19.11" +"@esbuild/linux-loong64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-loong64@npm:0.19.12" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -10722,9 +10722,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-mips64el@npm:0.19.11" +"@esbuild/linux-mips64el@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-mips64el@npm:0.19.12" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -10743,9 +10743,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-ppc64@npm:0.19.11" +"@esbuild/linux-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ppc64@npm:0.19.12" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -10764,9 +10764,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-riscv64@npm:0.19.11" +"@esbuild/linux-riscv64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-riscv64@npm:0.19.12" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -10785,9 +10785,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-s390x@npm:0.19.11" +"@esbuild/linux-s390x@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-s390x@npm:0.19.12" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -10806,9 +10806,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-x64@npm:0.19.11" +"@esbuild/linux-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-x64@npm:0.19.12" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -10827,9 +10827,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/netbsd-x64@npm:0.19.11" +"@esbuild/netbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/netbsd-x64@npm:0.19.12" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -10848,9 +10848,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/openbsd-x64@npm:0.19.11" +"@esbuild/openbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/openbsd-x64@npm:0.19.12" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -10869,9 +10869,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/sunos-x64@npm:0.19.11" +"@esbuild/sunos-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/sunos-x64@npm:0.19.12" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -10890,9 +10890,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-arm64@npm:0.19.11" +"@esbuild/win32-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-arm64@npm:0.19.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -10911,9 +10911,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-ia32@npm:0.19.11" +"@esbuild/win32-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-ia32@npm:0.19.12" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -10932,9 +10932,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-x64@npm:0.19.11" +"@esbuild/win32-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-x64@npm:0.19.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -25767,32 +25767,32 @@ __metadata: linkType: hard "esbuild@npm:^0.19.0, esbuild@npm:~0.19.10": - version: 0.19.11 - resolution: "esbuild@npm:0.19.11" + version: 0.19.12 + resolution: "esbuild@npm:0.19.12" dependencies: - "@esbuild/aix-ppc64": 0.19.11 - "@esbuild/android-arm": 0.19.11 - "@esbuild/android-arm64": 0.19.11 - "@esbuild/android-x64": 0.19.11 - "@esbuild/darwin-arm64": 0.19.11 - "@esbuild/darwin-x64": 0.19.11 - "@esbuild/freebsd-arm64": 0.19.11 - "@esbuild/freebsd-x64": 0.19.11 - "@esbuild/linux-arm": 0.19.11 - "@esbuild/linux-arm64": 0.19.11 - "@esbuild/linux-ia32": 0.19.11 - "@esbuild/linux-loong64": 0.19.11 - "@esbuild/linux-mips64el": 0.19.11 - "@esbuild/linux-ppc64": 0.19.11 - "@esbuild/linux-riscv64": 0.19.11 - "@esbuild/linux-s390x": 0.19.11 - "@esbuild/linux-x64": 0.19.11 - "@esbuild/netbsd-x64": 0.19.11 - "@esbuild/openbsd-x64": 0.19.11 - "@esbuild/sunos-x64": 0.19.11 - "@esbuild/win32-arm64": 0.19.11 - "@esbuild/win32-ia32": 0.19.11 - "@esbuild/win32-x64": 0.19.11 + "@esbuild/aix-ppc64": 0.19.12 + "@esbuild/android-arm": 0.19.12 + "@esbuild/android-arm64": 0.19.12 + "@esbuild/android-x64": 0.19.12 + "@esbuild/darwin-arm64": 0.19.12 + "@esbuild/darwin-x64": 0.19.12 + "@esbuild/freebsd-arm64": 0.19.12 + "@esbuild/freebsd-x64": 0.19.12 + "@esbuild/linux-arm": 0.19.12 + "@esbuild/linux-arm64": 0.19.12 + "@esbuild/linux-ia32": 0.19.12 + "@esbuild/linux-loong64": 0.19.12 + "@esbuild/linux-mips64el": 0.19.12 + "@esbuild/linux-ppc64": 0.19.12 + "@esbuild/linux-riscv64": 0.19.12 + "@esbuild/linux-s390x": 0.19.12 + "@esbuild/linux-x64": 0.19.12 + "@esbuild/netbsd-x64": 0.19.12 + "@esbuild/openbsd-x64": 0.19.12 + "@esbuild/sunos-x64": 0.19.12 + "@esbuild/win32-arm64": 0.19.12 + "@esbuild/win32-ia32": 0.19.12 + "@esbuild/win32-x64": 0.19.12 dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -25842,7 +25842,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: ae949a796d1d06b55275ae7491ce137857468f69a93d8cc9c0943d2a701ac54e14dbb250a2ba56f2ad98283669578f1ec3bd85a4681910a5ff29a2470c3bd62c + checksum: 2936e29107b43e65a775b78b7bc66ddd7d76febd73840ac7e825fb22b65029422ff51038a08d19b05154f543584bd3afe7d1ef1c63900429475b17fbe61cb61f languageName: node linkType: hard From 6021b2f437603ece1e03ef23c6fd930a24b23be7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:27:17 +0000 Subject: [PATCH 37/39] chore(deps): update dependency msw to v2.1.5 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5a15adff92..cb7009077e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12837,9 +12837,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.25.14": - version: 0.25.14 - resolution: "@mswjs/interceptors@npm:0.25.14" +"@mswjs/interceptors@npm:^0.25.15": + version: 0.25.15 + resolution: "@mswjs/interceptors@npm:0.25.15" dependencies: "@open-draft/deferred-promise": ^2.2.0 "@open-draft/logger": ^0.3.0 @@ -12847,7 +12847,7 @@ __metadata: is-node-process: ^1.2.0 outvariant: ^1.2.1 strict-event-emitter: ^0.5.1 - checksum: caf9513cf6848ff0c3f1402abf881d3c1333f68fae54076cfd3fd919b7edb709769342bd3afd2a60ef4ae698ef47776b6203e0ea6a5d8e788e97eda7ed9dbbd4 + checksum: dbe43f2df398bbe48ee5ea4ecf055144c9c5689218e5955b01378ea084044dab992d1b434d3533e75df044030a976402e53ec65d743c2619e024defd75ffc076 languageName: node linkType: hard @@ -34633,13 +34633,13 @@ __metadata: linkType: hard "msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.1.4 - resolution: "msw@npm:2.1.4" + version: 2.1.5 + resolution: "msw@npm:2.1.5" dependencies: "@bundled-es-modules/cookie": ^2.0.0 "@bundled-es-modules/statuses": ^1.0.1 "@mswjs/cookies": ^1.1.0 - "@mswjs/interceptors": ^0.25.14 + "@mswjs/interceptors": ^0.25.15 "@open-draft/until": ^2.1.0 "@types/cookie": ^0.6.0 "@types/statuses": ^2.0.4 @@ -34661,7 +34661,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: da8aaf9682ac48a635966beef9add9493297de797b266066bcd8ae0c2708488b81558251412e41489511a63deda1774b42e28197e9b73ddf14d9ecf8bb916e7a + checksum: 19a54a25baa584d1bafa219e1c2245073688eeed6ea5330f27c868a2f279390ee4fbd8a13d64cf0a056149d7e2faa300f901959bab8854e010f29368524d7c4f languageName: node linkType: hard From 08804c361ccc0e05b6daedd704ce78401fd5ab0e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 26 Jan 2024 18:18:54 +0100 Subject: [PATCH 38/39] cli: fix module discovery race Signed-off-by: Patrik Oldsberg --- .changeset/two-geese-explain.md | 5 +++++ packages/cli/package.json | 1 + .../cli/src/lib/bundler/packageDetection.ts | 18 ++++++++++++------ yarn.lock | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .changeset/two-geese-explain.md diff --git a/.changeset/two-geese-explain.md b/.changeset/two-geese-explain.md new file mode 100644 index 0000000000..12ae307cfe --- /dev/null +++ b/.changeset/two-geese-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed an issue that would cause an invalid `__backstage-autodetected-plugins__.js` to be written when using experimental module discovery. diff --git a/packages/cli/package.json b/packages/cli/package.json index 5ca7d675d3..96dc2e5db8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -112,6 +112,7 @@ "node-libs-browser": "^2.2.1", "npm-packlist": "^5.0.0", "ora": "^5.3.0", + "p-queue": "^6.6.2", "postcss": "^8.1.0", "process": "^0.11.10", "react-dev-utils": "^12.0.0-next.60", diff --git a/packages/cli/src/lib/bundler/packageDetection.ts b/packages/cli/src/lib/bundler/packageDetection.ts index 3aa0955c06..0c357fe79e 100644 --- a/packages/cli/src/lib/bundler/packageDetection.ts +++ b/packages/cli/src/lib/bundler/packageDetection.ts @@ -18,6 +18,7 @@ import { BackstagePackageJson } from '@backstage/cli-node'; import { Config, ConfigReader } from '@backstage/config'; import chokidar from 'chokidar'; import fs from 'fs-extra'; +import PQueue from 'p-queue'; import { join as joinPath, resolve as resolvePath } from 'path'; import { paths as cliPaths } from '../paths'; @@ -104,6 +105,9 @@ async function detectPackages( }); } +// Make sure we're not issuing multiple writes at the same time, which can cause partial overwrites +const writeQueue = new PQueue({ concurrency: 1 }); + async function writeDetectedPackagesModule( pkgs: { name: string; export?: string; import: string }[], ) { @@ -116,13 +120,15 @@ async function writeDetectedPackagesModule( ) .join(','); - await fs.writeFile( - joinPath( - cliPaths.targetRoot, - 'node_modules', - `${DETECTED_MODULES_MODULE_NAME}.js`, + await writeQueue.add(() => + fs.writeFile( + joinPath( + cliPaths.targetRoot, + 'node_modules', + `${DETECTED_MODULES_MODULE_NAME}.js`, + ), + `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`, ), - `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`, ); } diff --git a/yarn.lock b/yarn.lock index 1c83813e10..ad55841387 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3649,6 +3649,7 @@ __metadata: nodemon: ^3.0.1 npm-packlist: ^5.0.0 ora: ^5.3.0 + p-queue: ^6.6.2 postcss: ^8.1.0 process: ^0.11.10 react-dev-utils: ^12.0.0-next.60 From bc907df2204bf8d1821156a42046a8f8ae516774 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:27:31 +0000 Subject: [PATCH 39/39] fix(deps): update dependency @codemirror/view to v6.23.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 42811acc21..d3484630d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10179,13 +10179,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.23.0": - version: 6.23.0 - resolution: "@codemirror/view@npm:6.23.0" + version: 6.23.1 + resolution: "@codemirror/view@npm:6.23.1" dependencies: "@codemirror/state": ^6.4.0 style-mod: ^4.1.0 w3c-keyname: ^2.2.4 - checksum: 6e5f2314a3da2c724dc6a525654d949d3f2fcf7009f4d85f980d52ddc885c8969717e903ca1d9132afbe7c524af5d19bff8285fd394106282a965ae83aa47db4 + checksum: 5ea3ba5761c574e1f6e1f1058cb452189c890982a77991606d0ae40da3c6fff77f7c7fc3c43fa78d62677ccdfa65dbc56175706b793e34ad4ec7a63b21e8c18e languageName: node linkType: hard