@backstage/plugin-catalog-backend-module-github 0.14.0 → 0.14.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/alpha.cjs.js.map +1 -1
  3. package/dist/analyzers/GithubLocationAnalyzer.cjs.js.map +1 -1
  4. package/dist/deprecated.cjs.js +1 -0
  5. package/dist/deprecated.cjs.js.map +1 -1
  6. package/dist/events/GithubScmEventsBridge.cjs.js.map +1 -1
  7. package/dist/events/analyzeGithubWebhookEvent.cjs.js.map +1 -1
  8. package/dist/lib/annotation.cjs.js.map +1 -1
  9. package/dist/lib/config.cjs.js.map +1 -1
  10. package/dist/lib/defaultTransformers.cjs.js.map +1 -1
  11. package/dist/lib/github.cjs.js.map +1 -1
  12. package/dist/lib/guards.cjs.js.map +1 -1
  13. package/dist/lib/org.cjs.js.map +1 -1
  14. package/dist/lib/util.cjs.js.map +1 -1
  15. package/dist/lib/withLocations.cjs.js.map +1 -1
  16. package/dist/module/githubCatalogModule.cjs.js.map +1 -1
  17. package/dist/processors/GithubDiscoveryProcessor.cjs.js.map +1 -1
  18. package/dist/processors/GithubMultiOrgReaderProcessor.cjs.js +1 -0
  19. package/dist/processors/GithubMultiOrgReaderProcessor.cjs.js.map +1 -1
  20. package/dist/processors/GithubOrgReaderProcessor.cjs.js.map +1 -1
  21. package/dist/providers/GithubEntityProvider.cjs.js.map +1 -1
  22. package/dist/providers/GithubEntityProviderConfig.cjs.js.map +1 -1
  23. package/dist/providers/GithubMultiOrgEntityProvider.cjs.js +1 -0
  24. package/dist/providers/GithubMultiOrgEntityProvider.cjs.js.map +1 -1
  25. package/dist/providers/GithubOrgEntityProvider.cjs.js +1 -0
  26. package/dist/providers/GithubOrgEntityProvider.cjs.js.map +1 -1
  27. package/dist/util/octokitProviderService.cjs.js.map +1 -1
  28. package/package.json +17 -17
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @backstage/plugin-catalog-backend-module-github
2
2
 
3
+ ## 0.14.1-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/backend-plugin-api@1.11.0-next.1
9
+ - @backstage/plugin-catalog-node@2.2.6-next.1
10
+ - @backstage/plugin-events-node@0.4.27-next.1
11
+
12
+ ## 0.14.1-next.0
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies
17
+ - @backstage/integration@2.1.3-next.0
18
+ - @backstage/config@1.3.10-next.0
19
+ - @backstage/errors@1.3.2-next.0
20
+ - @backstage/backend-plugin-api@1.10.2-next.0
21
+ - @backstage/catalog-model@1.10.2-next.0
22
+ - @backstage/plugin-catalog-node@2.2.6-next.0
23
+ - @backstage/plugin-events-node@0.4.27-next.0
24
+ - @backstage/plugin-catalog-common@1.2.1-next.0
25
+
3
26
  ## 0.14.0
4
27
 
5
28
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { default as feature } from './module';\n\n/** @alpha */\nconst _feature = feature;\nexport default _feature;\n"],"names":["feature"],"mappings":";;;;;;AAmBA,MAAM,QAAA,GAAWA;;;;"}
1
+ {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { default as feature } from './module';\n\n/** @alpha */\nconst _feature = feature;\nexport default _feature;\n"],"names":["feature"],"mappings":";;;;;;AAmBA,MAAM,QAAA,GAAWA;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"GithubLocationAnalyzer.cjs.js","sources":["../../src/analyzers/GithubLocationAnalyzer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DefaultGithubCredentialsProvider,\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n ScmIntegrations,\n} from '@backstage/integration';\nimport { Octokit } from '@octokit/rest';\nimport { isEmpty, trimEnd } from 'lodash';\nimport parseGitUrl from 'git-url-parse';\nimport {\n AnalyzeOptions,\n CatalogService,\n ScmLocationAnalyzer,\n} from '@backstage/plugin-catalog-node';\nimport { Config } from '@backstage/config';\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport { extname } from 'node:path';\n\n/** @public */\nexport type GithubLocationAnalyzerOptions = {\n config: Config;\n auth: AuthService;\n githubCredentialsProvider?: GithubCredentialsProvider;\n catalog: CatalogService;\n};\n\n/** @public */\nexport class GithubLocationAnalyzer implements ScmLocationAnalyzer {\n private readonly catalogClient: CatalogService;\n private readonly githubCredentialsProvider: GithubCredentialsProvider;\n private readonly integrations: ScmIntegrationRegistry;\n private readonly auth: AuthService;\n\n constructor(options: GithubLocationAnalyzerOptions) {\n this.catalogClient = options.catalog;\n this.integrations = ScmIntegrations.fromConfig(options.config);\n this.githubCredentialsProvider =\n options.githubCredentialsProvider ||\n DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);\n\n this.auth = options.auth;\n }\n\n supports(url: string) {\n const integration = this.integrations.byUrl(url);\n return integration?.type === 'github';\n }\n\n async analyze(options: AnalyzeOptions) {\n const { url, catalogFilename } = options;\n const { owner, name: repo } = parseGitUrl(url);\n\n const catalogFile = catalogFilename || 'catalog-info.yaml';\n const extension = extname(catalogFile);\n const extensionQuery = !isEmpty(extension)\n ? `extension:${extension.replace('.', '')}`\n : '';\n\n const query = `filename:${catalogFile} ${extensionQuery} repo:${owner}/${repo}`;\n\n const integration = this.integrations.github.byUrl(url);\n if (!integration) {\n throw new Error('Make sure you have a GitHub integration configured');\n }\n\n const { token: githubToken } =\n await this.githubCredentialsProvider.getCredentials({\n url,\n });\n\n const octokitClient = new Octokit({\n auth: githubToken,\n baseUrl: integration.config.apiBaseUrl,\n });\n\n const searchResult = await octokitClient.search\n .code({ q: query })\n .catch(e => {\n throw new Error(`Couldn't search repository for metadata file, ${e}`);\n });\n\n const exists = searchResult.data.total_count > 0;\n if (exists) {\n const repoInformation = await octokitClient.repos\n .get({ owner, repo })\n .catch(e => {\n throw new Error(`Couldn't fetch repo data, ${e}`);\n });\n const defaultBranch = repoInformation.data.default_branch;\n\n const result = await Promise.all(\n searchResult.data.items\n .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`)\n .map(async target => {\n const addLocationResult = await this.catalogClient.addLocation(\n {\n type: 'url',\n target,\n dryRun: true,\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n return addLocationResult.entities.map(e => ({\n location: { type: 'url', target },\n isRegistered: !!addLocationResult.exists,\n entity: e,\n }));\n }),\n );\n\n return { existing: result.flat() };\n }\n return { existing: [] };\n }\n}\n"],"names":["ScmIntegrations","DefaultGithubCredentialsProvider","parseGitUrl","extname","isEmpty","Octokit","trimEnd"],"mappings":"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"}
1
+ {"version":3,"file":"GithubLocationAnalyzer.cjs.js","sources":["../../src/analyzers/GithubLocationAnalyzer.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DefaultGithubCredentialsProvider,\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n ScmIntegrations,\n} from '@backstage/integration';\nimport { Octokit } from '@octokit/rest';\nimport { isEmpty, trimEnd } from 'lodash';\nimport parseGitUrl from 'git-url-parse';\nimport {\n AnalyzeOptions,\n CatalogService,\n ScmLocationAnalyzer,\n} from '@backstage/plugin-catalog-node';\nimport { Config } from '@backstage/config';\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport { extname } from 'node:path';\n\n/** @public */\nexport type GithubLocationAnalyzerOptions = {\n config: Config;\n auth: AuthService;\n githubCredentialsProvider?: GithubCredentialsProvider;\n catalog: CatalogService;\n};\n\n/** @public */\nexport class GithubLocationAnalyzer implements ScmLocationAnalyzer {\n private readonly catalogClient: CatalogService;\n private readonly githubCredentialsProvider: GithubCredentialsProvider;\n private readonly integrations: ScmIntegrationRegistry;\n private readonly auth: AuthService;\n\n constructor(options: GithubLocationAnalyzerOptions) {\n this.catalogClient = options.catalog;\n this.integrations = ScmIntegrations.fromConfig(options.config);\n this.githubCredentialsProvider =\n options.githubCredentialsProvider ||\n DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);\n\n this.auth = options.auth;\n }\n\n supports(url: string) {\n const integration = this.integrations.byUrl(url);\n return integration?.type === 'github';\n }\n\n async analyze(options: AnalyzeOptions) {\n const { url, catalogFilename } = options;\n const { owner, name: repo } = parseGitUrl(url);\n\n const catalogFile = catalogFilename || 'catalog-info.yaml';\n const extension = extname(catalogFile);\n const extensionQuery = !isEmpty(extension)\n ? `extension:${extension.replace('.', '')}`\n : '';\n\n const query = `filename:${catalogFile} ${extensionQuery} repo:${owner}/${repo}`;\n\n const integration = this.integrations.github.byUrl(url);\n if (!integration) {\n throw new Error('Make sure you have a GitHub integration configured');\n }\n\n const { token: githubToken } =\n await this.githubCredentialsProvider.getCredentials({\n url,\n });\n\n const octokitClient = new Octokit({\n auth: githubToken,\n baseUrl: integration.config.apiBaseUrl,\n });\n\n const searchResult = await octokitClient.search\n .code({ q: query })\n .catch(e => {\n throw new Error(`Couldn't search repository for metadata file, ${e}`);\n });\n\n const exists = searchResult.data.total_count > 0;\n if (exists) {\n const repoInformation = await octokitClient.repos\n .get({ owner, repo })\n .catch(e => {\n throw new Error(`Couldn't fetch repo data, ${e}`);\n });\n const defaultBranch = repoInformation.data.default_branch;\n\n const result = await Promise.all(\n searchResult.data.items\n .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`)\n .map(async target => {\n const addLocationResult = await this.catalogClient.addLocation(\n {\n type: 'url',\n target,\n dryRun: true,\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n return addLocationResult.entities.map(e => ({\n location: { type: 'url', target },\n isRegistered: !!addLocationResult.exists,\n entity: e,\n }));\n }),\n );\n\n return { existing: result.flat() };\n }\n return { existing: [] };\n }\n}\n"],"names":["ScmIntegrations","DefaultGithubCredentialsProvider","parseGitUrl","extname","isEmpty","Octokit","trimEnd"],"mappings":"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"}
@@ -18,6 +18,7 @@ class GitHubEntityProvider {
18
18
  constructor(delegate) {
19
19
  this.delegate = delegate;
20
20
  }
21
+ delegate;
21
22
  static fromConfig(config, options) {
22
23
  options.logger.warn(
23
24
  "[Deprecated] Please use GithubEntityProvider instead of GitHubEntityProvider."
@@ -1 +1 @@
1
- {"version":3,"file":"deprecated.cjs.js","sources":["../src/deprecated.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport { GithubEntityProvider } from './providers/GithubEntityProvider';\nimport {\n GithubOrgEntityProvider,\n GithubOrgEntityProviderOptions,\n} from './providers/GithubOrgEntityProvider';\nimport {\n LoggerService,\n SchedulerService,\n SchedulerServiceTaskRunner,\n} from '@backstage/backend-plugin-api';\n\n/**\n * @public\n * @deprecated Use {@link GithubOrgEntityProvider} instead.\n */\nexport class GitHubOrgEntityProvider extends GithubOrgEntityProvider {\n static fromConfig(config: Config, options: GitHubOrgEntityProviderOptions) {\n options.logger.warn(\n '[Deprecated] Use GithubOrgEntityProvider instead of GitHubOrgEntityProvider.',\n );\n return GithubOrgEntityProvider.fromConfig(\n config,\n options as GithubOrgEntityProviderOptions,\n ) as GitHubOrgEntityProvider;\n }\n}\n\n/**\n * @public\n * @deprecated Use {@link GithubOrgEntityProviderOptions} instead.\n */\nexport type GitHubOrgEntityProviderOptions = GithubOrgEntityProviderOptions;\n\n/**\n * @public\n * @deprecated Use {@link GithubEntityProvider} instead.\n */\nexport class GitHubEntityProvider implements EntityProvider {\n static fromConfig(\n config: Config,\n options: {\n logger: LoggerService;\n schedule?: SchedulerServiceTaskRunner;\n scheduler?: SchedulerService;\n },\n ): GitHubEntityProvider[] {\n options.logger.warn(\n '[Deprecated] Please use GithubEntityProvider instead of GitHubEntityProvider.',\n );\n return GithubEntityProvider.fromConfig(config, options).map(\n delegate => new GitHubEntityProvider(delegate),\n );\n }\n\n private constructor(private readonly delegate: GithubEntityProvider) {}\n\n connect(connection: EntityProviderConnection): Promise<void> {\n return this.delegate.connect(connection);\n }\n\n getProviderName(): string {\n return this.delegate.getProviderName();\n }\n\n refresh(logger: LoggerService): Promise<void> {\n return this.delegate.refresh(logger);\n }\n}\n"],"names":["GithubOrgEntityProvider","GithubEntityProvider"],"mappings":";;;;;AAoCO,MAAM,gCAAgCA,+CAAA,CAAwB;AAAA,EACnE,OAAO,UAAA,CAAW,MAAA,EAAgB,OAAA,EAAyC;AACzE,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAA;AAAA,MACb;AAAA,KACF;AACA,IAAA,OAAOA,+CAAA,CAAwB,UAAA;AAAA,MAC7B,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;AAYO,MAAM,oBAAA,CAA+C;AAAA,EAiBlD,YAA6B,QAAA,EAAgC;AAAhC,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAAiC;AAAA,EAhBtE,OAAO,UAAA,CACL,MAAA,EACA,OAAA,EAKwB;AACxB,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAA;AAAA,MACb;AAAA,KACF;AACA,IAAA,OAAOC,yCAAA,CAAqB,UAAA,CAAW,MAAA,EAAQ,OAAO,CAAA,CAAE,GAAA;AAAA,MACtD,CAAA,QAAA,KAAY,IAAI,oBAAA,CAAqB,QAAQ;AAAA,KAC/C;AAAA,EACF;AAAA,EAIA,QAAQ,UAAA,EAAqD;AAC3D,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,UAAU,CAAA;AAAA,EACzC;AAAA,EAEA,eAAA,GAA0B;AACxB,IAAA,OAAO,IAAA,CAAK,SAAS,eAAA,EAAgB;AAAA,EACvC;AAAA,EAEA,QAAQ,MAAA,EAAsC;AAC5C,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,MAAM,CAAA;AAAA,EACrC;AACF;;;;;"}
1
+ {"version":3,"file":"deprecated.cjs.js","sources":["../src/deprecated.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport { GithubEntityProvider } from './providers/GithubEntityProvider';\nimport {\n GithubOrgEntityProvider,\n GithubOrgEntityProviderOptions,\n} from './providers/GithubOrgEntityProvider';\nimport {\n LoggerService,\n SchedulerService,\n SchedulerServiceTaskRunner,\n} from '@backstage/backend-plugin-api';\n\n/**\n * @public\n * @deprecated Use {@link GithubOrgEntityProvider} instead.\n */\nexport class GitHubOrgEntityProvider extends GithubOrgEntityProvider {\n static fromConfig(config: Config, options: GitHubOrgEntityProviderOptions) {\n options.logger.warn(\n '[Deprecated] Use GithubOrgEntityProvider instead of GitHubOrgEntityProvider.',\n );\n return GithubOrgEntityProvider.fromConfig(\n config,\n options as GithubOrgEntityProviderOptions,\n ) as GitHubOrgEntityProvider;\n }\n}\n\n/**\n * @public\n * @deprecated Use {@link GithubOrgEntityProviderOptions} instead.\n */\nexport type GitHubOrgEntityProviderOptions = GithubOrgEntityProviderOptions;\n\n/**\n * @public\n * @deprecated Use {@link GithubEntityProvider} instead.\n */\nexport class GitHubEntityProvider implements EntityProvider {\n static fromConfig(\n config: Config,\n options: {\n logger: LoggerService;\n schedule?: SchedulerServiceTaskRunner;\n scheduler?: SchedulerService;\n },\n ): GitHubEntityProvider[] {\n options.logger.warn(\n '[Deprecated] Please use GithubEntityProvider instead of GitHubEntityProvider.',\n );\n return GithubEntityProvider.fromConfig(config, options).map(\n delegate => new GitHubEntityProvider(delegate),\n );\n }\n\n private constructor(private readonly delegate: GithubEntityProvider) {}\n\n connect(connection: EntityProviderConnection): Promise<void> {\n return this.delegate.connect(connection);\n }\n\n getProviderName(): string {\n return this.delegate.getProviderName();\n }\n\n refresh(logger: LoggerService): Promise<void> {\n return this.delegate.refresh(logger);\n }\n}\n"],"names":["GithubOrgEntityProvider","GithubEntityProvider"],"mappings":";;;;;AAoCO,MAAM,gCAAgCA,+CAAA,CAAwB;AAAA,EACnE,OAAO,UAAA,CAAW,MAAA,EAAgB,OAAA,EAAyC;AACzE,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAA;AAAA,MACb;AAAA,KACF;AACA,IAAA,OAAOA,+CAAA,CAAwB,UAAA;AAAA,MAC7B,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;AAYO,MAAM,oBAAA,CAA+C;AAAA,EAiBlD,YAA6B,QAAA,EAAgC;AAAhC,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAAiC;AAAA,EAAjC,QAAA;AAAA,EAhBrC,OAAO,UAAA,CACL,MAAA,EACA,OAAA,EAKwB;AACxB,IAAA,OAAA,CAAQ,MAAA,CAAO,IAAA;AAAA,MACb;AAAA,KACF;AACA,IAAA,OAAOC,yCAAA,CAAqB,UAAA,CAAW,MAAA,EAAQ,OAAO,CAAA,CAAE,GAAA;AAAA,MACtD,CAAA,QAAA,KAAY,IAAI,oBAAA,CAAqB,QAAQ;AAAA,KAC/C;AAAA,EACF;AAAA,EAIA,QAAQ,UAAA,EAAqD;AAC3D,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,UAAU,CAAA;AAAA,EACzC;AAAA,EAEA,eAAA,GAA0B;AACxB,IAAA,OAAO,IAAA,CAAK,SAAS,eAAA,EAAgB;AAAA,EACvC;AAAA,EAEA,QAAQ,MAAA,EAAsC;AAC5C,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,MAAM,CAAA;AAAA,EACrC;AACF;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"GithubScmEventsBridge.cjs.js","sources":["../../src/events/GithubScmEventsBridge.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { CatalogScmEventsService } from '@backstage/plugin-catalog-node/alpha';\nimport { EventParams, EventsService } from '@backstage/plugin-events-node';\nimport { analyzeGithubWebhookEvent } from './analyzeGithubWebhookEvent';\nimport { OctokitProviderService } from '../util/octokitProviderService';\n\n/**\n * Takes GitHub webhook events, analyzes them, and publishes them as catalog SCM\n * events that entity providers and others can subscribe to.\n */\nexport class GithubScmEventsBridge {\n readonly #logger: LoggerService;\n readonly #events: EventsService;\n readonly #octokitProvider: OctokitProviderService;\n readonly #catalogScmEvents: CatalogScmEventsService;\n #shuttingDown: boolean;\n #pendingPublish: Promise<void> | undefined;\n\n constructor(options: {\n logger: LoggerService;\n events: EventsService;\n octokitProvider: OctokitProviderService;\n catalogScmEvents: CatalogScmEventsService;\n }) {\n this.#logger = options.logger;\n this.#events = options.events;\n this.#octokitProvider = options.octokitProvider;\n this.#catalogScmEvents = options.catalogScmEvents;\n this.#shuttingDown = false;\n }\n\n async start() {\n await this.#events.subscribe({\n id: 'catalog-github-scm-events-bridge',\n topics: ['github'],\n onEvent: this.#onEvent.bind(this),\n });\n }\n\n async stop() {\n this.#shuttingDown = true;\n await this.#pendingPublish;\n }\n\n async #onEvent(params: EventParams): Promise<void> {\n const eventType = params.metadata?.['x-github-event'] as string | undefined;\n const eventPayload = params.eventPayload;\n if (!eventType || !eventPayload) {\n return;\n }\n\n while (this.#pendingPublish) {\n await this.#pendingPublish;\n }\n\n if (this.#shuttingDown) {\n this.#logger.warn(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because the bridge is shutting down`,\n );\n return;\n }\n\n this.#pendingPublish = Promise.resolve().then(async () => {\n try {\n const output = await analyzeGithubWebhookEvent(\n eventType,\n eventPayload,\n {\n octokitProvider: this.#octokitProvider,\n logger: this.#logger,\n isRelevantPath: path =>\n path.endsWith('.yaml') || path.endsWith('.yml'),\n },\n );\n\n if (output.result === 'ok') {\n await this.#catalogScmEvents.publish(output.events);\n } else if (output.result === 'ignored') {\n this.#logger.debug(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is ignored: ${output.reason}`,\n );\n } else if (output.result === 'aborted') {\n this.#logger.warn(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is aborted: ${output.reason}`,\n );\n } else if (output.result === 'unsupported-event') {\n this.#logger.debug(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is unsupported: ${output.event}`,\n );\n }\n } catch (error) {\n this.#logger.warn(\n `Failed to handle GitHub webhook event of type \"${eventType}\"`,\n error,\n );\n } finally {\n this.#pendingPublish = undefined;\n }\n });\n\n await this.#pendingPublish;\n }\n}\n"],"names":["analyzeGithubWebhookEvent"],"mappings":";;;;AA0BO,MAAM,qBAAA,CAAsB;AAAA,EACxB,OAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA;AAAA,EACA,iBAAA;AAAA,EACT,aAAA;AAAA,EACA,eAAA;AAAA,EAEA,YAAY,OAAA,EAKT;AACD,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,MAAA;AACvB,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,MAAA;AACvB,IAAA,IAAA,CAAK,mBAAmB,OAAA,CAAQ,eAAA;AAChC,IAAA,IAAA,CAAK,oBAAoB,OAAA,CAAQ,gBAAA;AACjC,IAAA,IAAA,CAAK,aAAA,GAAgB,KAAA;AAAA,EACvB;AAAA,EAEA,MAAM,KAAA,GAAQ;AACZ,IAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,CAAU;AAAA,MAC3B,EAAA,EAAI,kCAAA;AAAA,MACJ,MAAA,EAAQ,CAAC,QAAQ,CAAA;AAAA,MACjB,OAAA,EAAS,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,IAAI;AAAA,KACjC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,IAAA,GAAO;AACX,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AACrB,IAAA,MAAM,IAAA,CAAK,eAAA;AAAA,EACb;AAAA,EAEA,MAAM,SAAS,MAAA,EAAoC;AACjD,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,GAAW,gBAAgB,CAAA;AACpD,IAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,YAAA,EAAc;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,OAAO,KAAK,eAAA,EAAiB;AAC3B,MAAA,MAAM,IAAA,CAAK,eAAA;AAAA,IACb;AAEA,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAA,IAAA,CAAK,OAAA,CAAQ,IAAA;AAAA,QACX,CAAA,uCAAA,EAA0C,SAAS,CAAA,YAAA,EAAe,MAAA,CAAO,KAAK,CAAA,qCAAA;AAAA,OAChF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,eAAA,GAAkB,OAAA,CAAQ,OAAA,EAAQ,CAAE,KAAK,YAAY;AACxD,MAAA,IAAI;AACF,QAAA,MAAM,SAAS,MAAMA,mDAAA;AAAA,UACnB,SAAA;AAAA,UACA,YAAA;AAAA,UACA;AAAA,YACE,iBAAiB,IAAA,CAAK,gBAAA;AAAA,YACtB,QAAQ,IAAA,CAAK,OAAA;AAAA,YACb,cAAA,EAAgB,UACd,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,IAAK,IAAA,CAAK,SAAS,MAAM;AAAA;AAClD,SACF;AAEA,QAAA,IAAI,MAAA,CAAO,WAAW,IAAA,EAAM;AAC1B,UAAA,MAAM,IAAA,CAAK,iBAAA,CAAkB,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA;AAAA,QACpD,CAAA,MAAA,IAAW,MAAA,CAAO,MAAA,KAAW,SAAA,EAAW;AACtC,UAAA,IAAA,CAAK,OAAA,CAAQ,KAAA;AAAA,YACX,0CAA0C,SAAS,CAAA,YAAA,EAAe,OAAO,KAAK,CAAA,yBAAA,EAA4B,OAAO,MAAM,CAAA;AAAA,WACzH;AAAA,QACF,CAAA,MAAA,IAAW,MAAA,CAAO,MAAA,KAAW,SAAA,EAAW;AACtC,UAAA,IAAA,CAAK,OAAA,CAAQ,IAAA;AAAA,YACX,0CAA0C,SAAS,CAAA,YAAA,EAAe,OAAO,KAAK,CAAA,yBAAA,EAA4B,OAAO,MAAM,CAAA;AAAA,WACzH;AAAA,QACF,CAAA,MAAA,IAAW,MAAA,CAAO,MAAA,KAAW,mBAAA,EAAqB;AAChD,UAAA,IAAA,CAAK,OAAA,CAAQ,KAAA;AAAA,YACX,0CAA0C,SAAS,CAAA,YAAA,EAAe,OAAO,KAAK,CAAA,6BAAA,EAAgC,OAAO,KAAK,CAAA;AAAA,WAC5H;AAAA,QACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,IAAA,CAAK,OAAA,CAAQ,IAAA;AAAA,UACX,kDAAkD,SAAS,CAAA,CAAA,CAAA;AAAA,UAC3D;AAAA,SACF;AAAA,MACF,CAAA,SAAE;AACA,QAAA,IAAA,CAAK,eAAA,GAAkB,MAAA;AAAA,MACzB;AAAA,IACF,CAAC,CAAA;AAED,IAAA,MAAM,IAAA,CAAK,eAAA;AAAA,EACb;AACF;;;;"}
1
+ {"version":3,"file":"GithubScmEventsBridge.cjs.js","sources":["../../src/events/GithubScmEventsBridge.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { CatalogScmEventsService } from '@backstage/plugin-catalog-node/alpha';\nimport { EventParams, EventsService } from '@backstage/plugin-events-node';\nimport { analyzeGithubWebhookEvent } from './analyzeGithubWebhookEvent';\nimport { OctokitProviderService } from '../util/octokitProviderService';\n\n/**\n * Takes GitHub webhook events, analyzes them, and publishes them as catalog SCM\n * events that entity providers and others can subscribe to.\n */\nexport class GithubScmEventsBridge {\n readonly #logger: LoggerService;\n readonly #events: EventsService;\n readonly #octokitProvider: OctokitProviderService;\n readonly #catalogScmEvents: CatalogScmEventsService;\n #shuttingDown: boolean;\n #pendingPublish: Promise<void> | undefined;\n\n constructor(options: {\n logger: LoggerService;\n events: EventsService;\n octokitProvider: OctokitProviderService;\n catalogScmEvents: CatalogScmEventsService;\n }) {\n this.#logger = options.logger;\n this.#events = options.events;\n this.#octokitProvider = options.octokitProvider;\n this.#catalogScmEvents = options.catalogScmEvents;\n this.#shuttingDown = false;\n }\n\n async start() {\n await this.#events.subscribe({\n id: 'catalog-github-scm-events-bridge',\n topics: ['github'],\n onEvent: this.#onEvent.bind(this),\n });\n }\n\n async stop() {\n this.#shuttingDown = true;\n await this.#pendingPublish;\n }\n\n async #onEvent(params: EventParams): Promise<void> {\n const eventType = params.metadata?.['x-github-event'] as string | undefined;\n const eventPayload = params.eventPayload;\n if (!eventType || !eventPayload) {\n return;\n }\n\n while (this.#pendingPublish) {\n await this.#pendingPublish;\n }\n\n if (this.#shuttingDown) {\n this.#logger.warn(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because the bridge is shutting down`,\n );\n return;\n }\n\n this.#pendingPublish = Promise.resolve().then(async () => {\n try {\n const output = await analyzeGithubWebhookEvent(\n eventType,\n eventPayload,\n {\n octokitProvider: this.#octokitProvider,\n logger: this.#logger,\n isRelevantPath: path =>\n path.endsWith('.yaml') || path.endsWith('.yml'),\n },\n );\n\n if (output.result === 'ok') {\n await this.#catalogScmEvents.publish(output.events);\n } else if (output.result === 'ignored') {\n this.#logger.debug(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is ignored: ${output.reason}`,\n );\n } else if (output.result === 'aborted') {\n this.#logger.warn(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is aborted: ${output.reason}`,\n );\n } else if (output.result === 'unsupported-event') {\n this.#logger.debug(\n `Skipping GitHub webhook event of type \"${eventType}\" on topic \"${params.topic}\" because it is unsupported: ${output.event}`,\n );\n }\n } catch (error) {\n this.#logger.warn(\n `Failed to handle GitHub webhook event of type \"${eventType}\"`,\n error,\n );\n } finally {\n this.#pendingPublish = undefined;\n }\n });\n\n await this.#pendingPublish;\n }\n}\n"],"names":["analyzeGithubWebhookEvent"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"analyzeGithubWebhookEvent.cjs.js","sources":["../../src/events/analyzeGithubWebhookEvent.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\nimport { CatalogScmEvent } from '@backstage/plugin-catalog-node/alpha';\nimport type {\n Commit,\n Organization,\n PushEvent,\n Repository,\n RepositoryArchivedEvent,\n RepositoryCreatedEvent,\n RepositoryDeletedEvent,\n RepositoryEditedEvent,\n RepositoryEvent,\n RepositoryPrivatizedEvent,\n RepositoryPublicizedEvent,\n RepositoryRenamedEvent,\n RepositoryTransferredEvent,\n RepositoryUnarchivedEvent,\n User,\n} from '@octokit/webhooks-types';\nimport { OctokitProviderService } from '../util/octokitProviderService';\nimport { Octokit } from 'octokit';\n\nexport interface AnalyzeWebhookEventOptions {\n octokitProvider: OctokitProviderService;\n logger: LoggerService;\n isRelevantPath: (path: string) => boolean;\n}\n\nexport type AnalyzeWebhookEventResult =\n | {\n result: 'unsupported-event';\n event: string;\n }\n | {\n result: 'ignored';\n reason: string;\n }\n | {\n result: 'aborted';\n reason: string;\n }\n | {\n result: 'ok';\n events: CatalogScmEvent[];\n };\n\n// Either a shorthand commit from a webhook event, or the full commit returned\n// by the client API\ntype AnyCommit =\n | Commit\n | Awaited<ReturnType<Octokit['rest']['repos']['getCommit']>>['data'];\n\n/**\n * Keeps track of intermediate state of individual paths while processing push events.\n */\ntype PathState =\n | {\n type: 'added';\n commit: AnyCommit;\n }\n | {\n type: 'removed';\n commit: AnyCommit;\n }\n | {\n type: 'modified';\n commit: AnyCommit;\n }\n | {\n type: 'renamed';\n fromPath: string;\n commit: AnyCommit;\n }\n | {\n type: 'changed';\n commit: AnyCommit;\n };\n\nfunction pathStateToCatalogScmEvent(\n path: string,\n event: PathState,\n repository: Repository,\n): CatalogScmEvent {\n const toBlobUrl = (p: string) =>\n `${repository.html_url}/blob/${repository.default_branch}/${p}`;\n\n const context = {\n commitUrl:\n 'html_url' in event.commit ? event.commit.html_url : event.commit.url,\n };\n\n switch (event.type) {\n case 'added':\n return {\n type: 'location.created',\n url: toBlobUrl(path),\n context,\n };\n case 'removed':\n return {\n type: 'location.deleted',\n url: toBlobUrl(path),\n context,\n };\n case 'modified':\n return {\n type: 'location.updated',\n url: toBlobUrl(path),\n context,\n };\n case 'renamed':\n return {\n type: 'location.moved',\n fromUrl: toBlobUrl(event.fromPath),\n toUrl: toBlobUrl(path),\n context,\n };\n case 'changed':\n return {\n type: 'location.updated',\n url: toBlobUrl(path),\n context,\n };\n default:\n // @ts-expect-error Intentionally expected, to check for exhaustive checking of the types\n throw new Error(`Unknown file event type: ${event.type}`);\n }\n}\n\n/**\n * Analyzes a GitHub webhook event and returns details about actions that the\n * event might require.\n */\nexport async function analyzeGithubWebhookEvent(\n eventType: string,\n eventPayload: unknown,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n if (\n !eventPayload ||\n typeof eventPayload !== 'object' ||\n Array.isArray(eventPayload)\n ) {\n throw new InputError('GitHub webhook event payload is not an object');\n }\n\n if (eventType === 'push') {\n return await onPushEvent(eventPayload as PushEvent, options);\n }\n\n if (eventType === 'repository') {\n const repositoryEvent = eventPayload as RepositoryEvent;\n const action = repositoryEvent.action;\n if (action === 'created') {\n return await onRepositoryCreatedEvent(repositoryEvent);\n } else if (action === 'deleted') {\n return await onRepositoryDeletedEvent(repositoryEvent);\n } else if (action === 'archived') {\n return await onRepositoryArchivedEvent(repositoryEvent);\n } else if (action === 'unarchived') {\n return await onRepositoryUnarchivedEvent(repositoryEvent);\n } else if (action === 'privatized') {\n return await onRepositoryPrivatizedEvent(repositoryEvent);\n } else if (action === 'publicized') {\n return await onRepositoryPublicizedEvent(repositoryEvent);\n } else if (action === 'renamed') {\n return await onRepositoryRenamedEvent(repositoryEvent);\n } else if (action === 'edited') {\n return await onRepositoryEditedEvent(repositoryEvent);\n } else if (action === 'transferred') {\n return await onRepositoryTransferredEvent(repositoryEvent);\n }\n return {\n result: 'unsupported-event',\n event: `${eventType}.${action}`,\n };\n }\n\n return {\n result: 'unsupported-event',\n event: eventType,\n };\n}\n\n// #region Push events\n\nasync function onPushEvent(\n event: PushEvent,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n const contextUrl = event.compare || '<unknown>';\n\n // NOTE: The following caveats are mentioned in https://docs.github.com/en/webhooks/webhook-events-and-payloads#push\n // * Events will not be created if more than 5000 branches are pushed at once.\n // * Events will not be created for tags when more than three tags are pushed at once.\n // * The commits array includes a maximum of 2048 commits. If necessary, you can use the Commits API to fetch additional commits.\n // TODO(freben): Do we need to support reading the commits using the separate API under some circumstances?\n if (event.commits.length >= 2048) {\n return {\n result: 'aborted',\n reason: `GitHub push event has too many commits (${event.commits.length}), assuming that this is not a complete push event: ${contextUrl}`,\n };\n }\n\n // We ignore any event that doesn't target the default branch as this\n // is where the metadata files should be stored for now.\n const defaultBranchRef = `refs/heads/${event.repository.default_branch}`;\n if (event.ref !== defaultBranchRef) {\n return {\n result: 'ignored',\n reason: `GitHub push event did not target the default branch, found \"${event.ref}\" but expected \"${defaultBranchRef}\": ${contextUrl}`,\n };\n }\n\n /*\n * STEP 1: Find interesting commits\n *\n * We go through the commits and exclude the ones that are not interesting. We\n * consider a commit interesting if it mentions any relevant paths. We don't\n * want to unnecessarily call out to the GitHub API for commits that don't\n * affect files that we care about.\n *\n * The raw webhook event only contains shorthand commit data, not the full set\n * of information. Notably it does not contain the \"files\" array that details\n * what precise type of action was taken on individual paths. Instead it\n * presents three summary arrays per commit: \"added\", \"removed\", and\n * \"modified\", that just hold path strings.\n */\n\n const interestingShorthandCommits = (event.commits ?? []).filter(\n commit =>\n commit.added?.some(options.isRelevantPath) ||\n commit.removed?.some(options.isRelevantPath) ||\n commit.modified?.some(options.isRelevantPath),\n );\n\n if (!interestingShorthandCommits.length) {\n return {\n result: 'ignored',\n reason: `GitHub push event did not affect any relevant paths: ${contextUrl}`,\n };\n }\n\n /*\n * STEP 2: Simple case\n *\n * We go through the interesting shorthand commits in time order and make a\n * basic assessment of what happened. Most of the time this will be enough to\n * determine that (for relevant paths) there were only additions or only\n * removals - and possibly some changes. In that case we don't have to pay the\n * cost of fetching the detailed commits, because it's safe to assume that\n * there were no complex renames/moves or similar.\n *\n * If we find that any path is mentioned more than once, we bail out and go to\n * the complex case below instead.\n */\n\n const hasAddedPaths = interestingShorthandCommits.some(\n commit => (commit.added ?? []).filter(options.isRelevantPath).length > 0,\n );\n const hasRemovedPaths = interestingShorthandCommits.some(\n commit => (commit.removed ?? []).filter(options.isRelevantPath).length > 0,\n );\n\n if (!(hasAddedPaths && hasRemovedPaths)) {\n const addedOrRemovedPaths = new Map<string, PathState>();\n const changedPaths = new Map<string, PathState>();\n\n for (const commit of interestingShorthandCommits) {\n for (const path of commit.modified?.filter(options.isRelevantPath) ??\n []) {\n if (!addedOrRemovedPaths.has(path)) {\n changedPaths.set(path, { type: 'changed', commit });\n }\n }\n for (const path of commit.added?.filter(options.isRelevantPath) ?? []) {\n changedPaths.delete(path);\n addedOrRemovedPaths.set(path, { type: 'added', commit });\n }\n for (const path of commit.removed?.filter(options.isRelevantPath) ?? []) {\n changedPaths.delete(path);\n addedOrRemovedPaths.set(path, { type: 'removed', commit });\n }\n }\n\n const allPaths = new Map([\n ...changedPaths.entries(),\n ...addedOrRemovedPaths.entries(),\n ]);\n\n return {\n result: 'ok',\n events: Array.from(allPaths.entries()).map(([path, e]) =>\n pathStateToCatalogScmEvent(path, e, event.repository),\n ),\n };\n }\n\n /*\n * STEP 3: Complex case\n *\n * There seem to be a more complex overall set of changes here. There are both\n * additions and removals that look interesting. Now we need to analyze the\n * commits more carefully, to see which changes constitute purely individual\n * additions/removals, and which ones are actually moves. We do this by\n * iterating through the commits as fetched from the remote (since they\n * contain richer information than the webhook), and computing the \"compound\"\n * outcome (e.g. an add followed by a remove of the same file can be ignored).\n */\n\n const pathState = new Map<string, PathState>();\n\n const octokit = await options.octokitProvider.getOctokit(\n event.repository.html_url,\n );\n\n for (const eventCommit of interestingShorthandCommits) {\n // As noted in the getCommit documentation, if there's a large number of\n // files in the commit then only at most 300 of them will be returned along\n // with pagination link header, and then going up to a total of at most\n // 3000 files. But we also want to use the convenient octokit API so we\n // paginate in this kind of clunky way and end whenever there's no more rel\n // next URL.\n let commit:\n | Awaited<ReturnType<Octokit['rest']['repos']['getCommit']>>['data']\n | undefined;\n for (let page = 1; page <= 10; ++page) {\n const response = await octokit.rest.repos.getCommit({\n page,\n per_page: 300,\n owner: event.repository.owner.login,\n repo: event.repository.name,\n ref: eventCommit.id,\n });\n\n if (!commit) {\n commit = response.data;\n } else {\n commit.files = [\n ...(commit.files ?? []),\n ...(response.data.files ?? []),\n ];\n }\n\n if (!response.headers?.link?.includes('rel=\"next\"')) {\n break;\n }\n }\n\n if (!commit) {\n throw new Error(`Failed to fetch commit for ${contextUrl}`);\n }\n\n // We somewhat wastefully track all paths here whether they seem initially\n // relevant or not, because we don't know yet whether they will be\n // moved/renamed into or out of relevance.\n for (const file of commit.files ?? []) {\n const previous = pathState.get(file.previous_filename || file.filename);\n let next: PathState | undefined;\n if (file.status === 'added') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'added', commit };\n } else if (previous.type === 'removed') {\n // Removed and then added again - assume changes\n next = { type: 'changed', commit };\n } else {\n // Should not happen; added/changed/moved -> added\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'removed') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'removed', commit };\n } else if (previous.type === 'added') {\n // It was first added and then removed - turn into noop\n next = undefined;\n } else if (previous.type === 'changed') {\n // Changed and then removed; convert to removed\n next = { type: 'removed', commit };\n } else if (previous.type === 'renamed') {\n // It was renamed and then removed - convert to removal of the ORIGINAL path\n if (!pathState.has(previous.fromPath)) {\n pathState.set(previous.fromPath, { type: 'removed', commit });\n }\n next = undefined;\n } else {\n // Should not happen; removed -> removed\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'renamed') {\n pathState.delete(file.previous_filename!);\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'renamed', fromPath: file.previous_filename!, commit };\n } else if (previous.type === 'added') {\n // It was first added and then moved - this sums to still just an add but in the new path\n next = { type: 'added', commit };\n } else if (previous.type === 'changed') {\n // Changed and then moved; convert to moved\n next = { type: 'renamed', fromPath: file.previous_filename!, commit };\n } else if (previous.type === 'renamed') {\n // It was renamed and then renamed again\n next = { type: 'renamed', fromPath: previous.fromPath, commit };\n } else {\n // Should not happen; removed -> renamed\n next = undefined;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'changed' || file.status === 'modified') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'changed', commit };\n } else if (previous.type === 'added') {\n // It was first added and then changed - still an add\n next = previous;\n } else if (previous.type === 'changed') {\n // Changed twice - still just a change\n next = previous;\n } else if (previous.type === 'renamed') {\n // Renamed and then changed - still a rename\n next = previous;\n } else {\n // Should not happen; removed -> changed but still keep the old state because it makes the most sense\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else {\n // remaining statuses are 'copied' and 'unchanged' - ignoring\n next = previous;\n }\n\n if (next) {\n pathState.set(file.filename, next);\n } else {\n pathState.delete(file.filename);\n }\n }\n }\n\n return {\n result: 'ok',\n events: Array.from(pathState.entries()).flatMap(([path, e]) =>\n options.isRelevantPath(path)\n ? [pathStateToCatalogScmEvent(path, e, event.repository)]\n : [],\n ),\n };\n}\n\n// #endregion\n// #region Repository events\n\nasync function onRepositoryArchivedEvent(\n event: RepositoryArchivedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryUnarchivedEvent(\n event: RepositoryUnarchivedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryPrivatizedEvent(\n event: RepositoryPrivatizedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryPublicizedEvent(\n event: RepositoryPublicizedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryCreatedEvent(\n event: RepositoryCreatedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.created',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryDeletedEvent(\n event: RepositoryDeletedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.deleted',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryRenamedEvent(\n event: RepositoryRenamedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n const oldUrlPrefix = `${event.repository.owner.html_url}/${event.changes.repository.name.from}`;\n const newUrlPrefix = `${event.repository.html_url}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n}\n\nasync function onRepositoryEditedEvent(\n event: RepositoryEditedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n if (event.changes.default_branch) {\n const oldUrlPrefix = `${event.repository.html_url}/blob/${event.changes.default_branch.from}`;\n const newUrlPrefix = `${event.repository.html_url}/blob/${event.repository.default_branch}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n }\n\n const changes = Object.keys(event.changes)\n .map(c => `'${c}'`)\n .join(', ');\n return {\n result: 'ignored',\n reason: `GitHub repository edited event not handled: [${changes}]`,\n };\n}\n\nasync function onRepositoryTransferredEvent(\n event: RepositoryTransferredEvent,\n): Promise<AnalyzeWebhookEventResult> {\n // TODO(freben): The web interface allows renaming while transferring, but\n // we do not yet have a way of detecting that because weirdly, the actual\n // webhooks that then get sent are\n //\n // - First a transferred event OLD_OWNER -> NEW_OWNER/NEW_REPO_NAME\n // (no mention of the OLD_REPO_NAME anywhere in the event payload)\n //\n // - Then a renamed event for NEW_OWNER of OLD_REPO_NAME -> NEW_REPO_NAME\n // (where the former of course never existed as far as we know;\n // no mention of the OLD_OWNER anywhere in the event payload)\n //\n // Handling this seems to need some form of state handling across events.\n //\n // Therefore, this code expects that OLD_REPO_NAME === NEW_REPO_NAME\n // and assumes that we did not perform a rename as part of the transfer.\n\n // The documentation states that there is an organization property in from,\n // but the types disagree. Hence the cast.\n // https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=transferred#repository\n const from = event.changes.owner.from as {\n user?: User;\n organization?: Organization;\n };\n\n let oldOwnerUrl = from.user?.html_url ?? from.organization?.html_url;\n if (!oldOwnerUrl) {\n // Some servers do not supply an html_url field, so we\n // construct it by removing the last two segments of the\n // target URL instead (which we know ends with \"/owner/repo\")\n const base = event.repository.html_url.split('/').slice(0, -2).join('/');\n oldOwnerUrl = `${base}/${from.user?.login ?? from.organization?.login}`;\n }\n\n const oldUrlPrefix = `${oldOwnerUrl}/${event.repository.name}`;\n const newUrlPrefix = `${event.repository.html_url}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n}\n\n// #endregion\n"],"names":["InputError"],"mappings":"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"}
1
+ {"version":3,"file":"analyzeGithubWebhookEvent.cjs.js","sources":["../../src/events/analyzeGithubWebhookEvent.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\nimport { CatalogScmEvent } from '@backstage/plugin-catalog-node/alpha';\nimport type {\n Commit,\n Organization,\n PushEvent,\n Repository,\n RepositoryArchivedEvent,\n RepositoryCreatedEvent,\n RepositoryDeletedEvent,\n RepositoryEditedEvent,\n RepositoryEvent,\n RepositoryPrivatizedEvent,\n RepositoryPublicizedEvent,\n RepositoryRenamedEvent,\n RepositoryTransferredEvent,\n RepositoryUnarchivedEvent,\n User,\n} from '@octokit/webhooks-types';\nimport { OctokitProviderService } from '../util/octokitProviderService';\nimport { Octokit } from 'octokit';\n\nexport interface AnalyzeWebhookEventOptions {\n octokitProvider: OctokitProviderService;\n logger: LoggerService;\n isRelevantPath: (path: string) => boolean;\n}\n\nexport type AnalyzeWebhookEventResult =\n | {\n result: 'unsupported-event';\n event: string;\n }\n | {\n result: 'ignored';\n reason: string;\n }\n | {\n result: 'aborted';\n reason: string;\n }\n | {\n result: 'ok';\n events: CatalogScmEvent[];\n };\n\n// Either a shorthand commit from a webhook event, or the full commit returned\n// by the client API\ntype AnyCommit =\n | Commit\n | Awaited<ReturnType<Octokit['rest']['repos']['getCommit']>>['data'];\n\n/**\n * Keeps track of intermediate state of individual paths while processing push events.\n */\ntype PathState =\n | {\n type: 'added';\n commit: AnyCommit;\n }\n | {\n type: 'removed';\n commit: AnyCommit;\n }\n | {\n type: 'modified';\n commit: AnyCommit;\n }\n | {\n type: 'renamed';\n fromPath: string;\n commit: AnyCommit;\n }\n | {\n type: 'changed';\n commit: AnyCommit;\n };\n\nfunction pathStateToCatalogScmEvent(\n path: string,\n event: PathState,\n repository: Repository,\n): CatalogScmEvent {\n const toBlobUrl = (p: string) =>\n `${repository.html_url}/blob/${repository.default_branch}/${p}`;\n\n const context = {\n commitUrl:\n 'html_url' in event.commit ? event.commit.html_url : event.commit.url,\n };\n\n switch (event.type) {\n case 'added':\n return {\n type: 'location.created',\n url: toBlobUrl(path),\n context,\n };\n case 'removed':\n return {\n type: 'location.deleted',\n url: toBlobUrl(path),\n context,\n };\n case 'modified':\n return {\n type: 'location.updated',\n url: toBlobUrl(path),\n context,\n };\n case 'renamed':\n return {\n type: 'location.moved',\n fromUrl: toBlobUrl(event.fromPath),\n toUrl: toBlobUrl(path),\n context,\n };\n case 'changed':\n return {\n type: 'location.updated',\n url: toBlobUrl(path),\n context,\n };\n default:\n // @ts-expect-error Intentionally expected, to check for exhaustive checking of the types\n throw new Error(`Unknown file event type: ${event.type}`);\n }\n}\n\n/**\n * Analyzes a GitHub webhook event and returns details about actions that the\n * event might require.\n */\nexport async function analyzeGithubWebhookEvent(\n eventType: string,\n eventPayload: unknown,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n if (\n !eventPayload ||\n typeof eventPayload !== 'object' ||\n Array.isArray(eventPayload)\n ) {\n throw new InputError('GitHub webhook event payload is not an object');\n }\n\n if (eventType === 'push') {\n return await onPushEvent(eventPayload as PushEvent, options);\n }\n\n if (eventType === 'repository') {\n const repositoryEvent = eventPayload as RepositoryEvent;\n const action = repositoryEvent.action;\n if (action === 'created') {\n return await onRepositoryCreatedEvent(repositoryEvent);\n } else if (action === 'deleted') {\n return await onRepositoryDeletedEvent(repositoryEvent);\n } else if (action === 'archived') {\n return await onRepositoryArchivedEvent(repositoryEvent);\n } else if (action === 'unarchived') {\n return await onRepositoryUnarchivedEvent(repositoryEvent);\n } else if (action === 'privatized') {\n return await onRepositoryPrivatizedEvent(repositoryEvent);\n } else if (action === 'publicized') {\n return await onRepositoryPublicizedEvent(repositoryEvent);\n } else if (action === 'renamed') {\n return await onRepositoryRenamedEvent(repositoryEvent);\n } else if (action === 'edited') {\n return await onRepositoryEditedEvent(repositoryEvent);\n } else if (action === 'transferred') {\n return await onRepositoryTransferredEvent(repositoryEvent);\n }\n return {\n result: 'unsupported-event',\n event: `${eventType}.${action}`,\n };\n }\n\n return {\n result: 'unsupported-event',\n event: eventType,\n };\n}\n\n// #region Push events\n\nasync function onPushEvent(\n event: PushEvent,\n options: AnalyzeWebhookEventOptions,\n): Promise<AnalyzeWebhookEventResult> {\n const contextUrl = event.compare || '<unknown>';\n\n // NOTE: The following caveats are mentioned in https://docs.github.com/en/webhooks/webhook-events-and-payloads#push\n // * Events will not be created if more than 5000 branches are pushed at once.\n // * Events will not be created for tags when more than three tags are pushed at once.\n // * The commits array includes a maximum of 2048 commits. If necessary, you can use the Commits API to fetch additional commits.\n // TODO(freben): Do we need to support reading the commits using the separate API under some circumstances?\n if (event.commits.length >= 2048) {\n return {\n result: 'aborted',\n reason: `GitHub push event has too many commits (${event.commits.length}), assuming that this is not a complete push event: ${contextUrl}`,\n };\n }\n\n // We ignore any event that doesn't target the default branch as this\n // is where the metadata files should be stored for now.\n const defaultBranchRef = `refs/heads/${event.repository.default_branch}`;\n if (event.ref !== defaultBranchRef) {\n return {\n result: 'ignored',\n reason: `GitHub push event did not target the default branch, found \"${event.ref}\" but expected \"${defaultBranchRef}\": ${contextUrl}`,\n };\n }\n\n /*\n * STEP 1: Find interesting commits\n *\n * We go through the commits and exclude the ones that are not interesting. We\n * consider a commit interesting if it mentions any relevant paths. We don't\n * want to unnecessarily call out to the GitHub API for commits that don't\n * affect files that we care about.\n *\n * The raw webhook event only contains shorthand commit data, not the full set\n * of information. Notably it does not contain the \"files\" array that details\n * what precise type of action was taken on individual paths. Instead it\n * presents three summary arrays per commit: \"added\", \"removed\", and\n * \"modified\", that just hold path strings.\n */\n\n const interestingShorthandCommits = (event.commits ?? []).filter(\n commit =>\n commit.added?.some(options.isRelevantPath) ||\n commit.removed?.some(options.isRelevantPath) ||\n commit.modified?.some(options.isRelevantPath),\n );\n\n if (!interestingShorthandCommits.length) {\n return {\n result: 'ignored',\n reason: `GitHub push event did not affect any relevant paths: ${contextUrl}`,\n };\n }\n\n /*\n * STEP 2: Simple case\n *\n * We go through the interesting shorthand commits in time order and make a\n * basic assessment of what happened. Most of the time this will be enough to\n * determine that (for relevant paths) there were only additions or only\n * removals - and possibly some changes. In that case we don't have to pay the\n * cost of fetching the detailed commits, because it's safe to assume that\n * there were no complex renames/moves or similar.\n *\n * If we find that any path is mentioned more than once, we bail out and go to\n * the complex case below instead.\n */\n\n const hasAddedPaths = interestingShorthandCommits.some(\n commit => (commit.added ?? []).filter(options.isRelevantPath).length > 0,\n );\n const hasRemovedPaths = interestingShorthandCommits.some(\n commit => (commit.removed ?? []).filter(options.isRelevantPath).length > 0,\n );\n\n if (!(hasAddedPaths && hasRemovedPaths)) {\n const addedOrRemovedPaths = new Map<string, PathState>();\n const changedPaths = new Map<string, PathState>();\n\n for (const commit of interestingShorthandCommits) {\n for (const path of commit.modified?.filter(options.isRelevantPath) ??\n []) {\n if (!addedOrRemovedPaths.has(path)) {\n changedPaths.set(path, { type: 'changed', commit });\n }\n }\n for (const path of commit.added?.filter(options.isRelevantPath) ?? []) {\n changedPaths.delete(path);\n addedOrRemovedPaths.set(path, { type: 'added', commit });\n }\n for (const path of commit.removed?.filter(options.isRelevantPath) ?? []) {\n changedPaths.delete(path);\n addedOrRemovedPaths.set(path, { type: 'removed', commit });\n }\n }\n\n const allPaths = new Map([\n ...changedPaths.entries(),\n ...addedOrRemovedPaths.entries(),\n ]);\n\n return {\n result: 'ok',\n events: Array.from(allPaths.entries()).map(([path, e]) =>\n pathStateToCatalogScmEvent(path, e, event.repository),\n ),\n };\n }\n\n /*\n * STEP 3: Complex case\n *\n * There seem to be a more complex overall set of changes here. There are both\n * additions and removals that look interesting. Now we need to analyze the\n * commits more carefully, to see which changes constitute purely individual\n * additions/removals, and which ones are actually moves. We do this by\n * iterating through the commits as fetched from the remote (since they\n * contain richer information than the webhook), and computing the \"compound\"\n * outcome (e.g. an add followed by a remove of the same file can be ignored).\n */\n\n const pathState = new Map<string, PathState>();\n\n const octokit = await options.octokitProvider.getOctokit(\n event.repository.html_url,\n );\n\n for (const eventCommit of interestingShorthandCommits) {\n // As noted in the getCommit documentation, if there's a large number of\n // files in the commit then only at most 300 of them will be returned along\n // with pagination link header, and then going up to a total of at most\n // 3000 files. But we also want to use the convenient octokit API so we\n // paginate in this kind of clunky way and end whenever there's no more rel\n // next URL.\n let commit:\n | Awaited<ReturnType<Octokit['rest']['repos']['getCommit']>>['data']\n | undefined;\n for (let page = 1; page <= 10; ++page) {\n const response = await octokit.rest.repos.getCommit({\n page,\n per_page: 300,\n owner: event.repository.owner.login,\n repo: event.repository.name,\n ref: eventCommit.id,\n });\n\n if (!commit) {\n commit = response.data;\n } else {\n commit.files = [\n ...(commit.files ?? []),\n ...(response.data.files ?? []),\n ];\n }\n\n if (!response.headers?.link?.includes('rel=\"next\"')) {\n break;\n }\n }\n\n if (!commit) {\n throw new Error(`Failed to fetch commit for ${contextUrl}`);\n }\n\n // We somewhat wastefully track all paths here whether they seem initially\n // relevant or not, because we don't know yet whether they will be\n // moved/renamed into or out of relevance.\n for (const file of commit.files ?? []) {\n const previous = pathState.get(file.previous_filename || file.filename);\n let next: PathState | undefined;\n if (file.status === 'added') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'added', commit };\n } else if (previous.type === 'removed') {\n // Removed and then added again - assume changes\n next = { type: 'changed', commit };\n } else {\n // Should not happen; added/changed/moved -> added\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'removed') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'removed', commit };\n } else if (previous.type === 'added') {\n // It was first added and then removed - turn into noop\n next = undefined;\n } else if (previous.type === 'changed') {\n // Changed and then removed; convert to removed\n next = { type: 'removed', commit };\n } else if (previous.type === 'renamed') {\n // It was renamed and then removed - convert to removal of the ORIGINAL path\n if (!pathState.has(previous.fromPath)) {\n pathState.set(previous.fromPath, { type: 'removed', commit });\n }\n next = undefined;\n } else {\n // Should not happen; removed -> removed\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'renamed') {\n pathState.delete(file.previous_filename!);\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'renamed', fromPath: file.previous_filename!, commit };\n } else if (previous.type === 'added') {\n // It was first added and then moved - this sums to still just an add but in the new path\n next = { type: 'added', commit };\n } else if (previous.type === 'changed') {\n // Changed and then moved; convert to moved\n next = { type: 'renamed', fromPath: file.previous_filename!, commit };\n } else if (previous.type === 'renamed') {\n // It was renamed and then renamed again\n next = { type: 'renamed', fromPath: previous.fromPath, commit };\n } else {\n // Should not happen; removed -> renamed\n next = undefined;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else if (file.status === 'changed' || file.status === 'modified') {\n if (!previous) {\n // First time we see this file in this set of commits\n next = { type: 'changed', commit };\n } else if (previous.type === 'added') {\n // It was first added and then changed - still an add\n next = previous;\n } else if (previous.type === 'changed') {\n // Changed twice - still just a change\n next = previous;\n } else if (previous.type === 'renamed') {\n // Renamed and then changed - still a rename\n next = previous;\n } else {\n // Should not happen; removed -> changed but still keep the old state because it makes the most sense\n next = previous;\n options.logger.debug(\n `Unexpected commit state transition from ${previous.type} to ${file.status} in ${event.compare}`,\n );\n }\n } else {\n // remaining statuses are 'copied' and 'unchanged' - ignoring\n next = previous;\n }\n\n if (next) {\n pathState.set(file.filename, next);\n } else {\n pathState.delete(file.filename);\n }\n }\n }\n\n return {\n result: 'ok',\n events: Array.from(pathState.entries()).flatMap(([path, e]) =>\n options.isRelevantPath(path)\n ? [pathStateToCatalogScmEvent(path, e, event.repository)]\n : [],\n ),\n };\n}\n\n// #endregion\n// #region Repository events\n\nasync function onRepositoryArchivedEvent(\n event: RepositoryArchivedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryUnarchivedEvent(\n event: RepositoryUnarchivedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryPrivatizedEvent(\n event: RepositoryPrivatizedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryPublicizedEvent(\n event: RepositoryPublicizedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.updated',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryCreatedEvent(\n event: RepositoryCreatedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.created',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryDeletedEvent(\n event: RepositoryDeletedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.deleted',\n url: event.repository.html_url,\n },\n ],\n };\n}\n\nasync function onRepositoryRenamedEvent(\n event: RepositoryRenamedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n const oldUrlPrefix = `${event.repository.owner.html_url}/${event.changes.repository.name.from}`;\n const newUrlPrefix = `${event.repository.html_url}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n}\n\nasync function onRepositoryEditedEvent(\n event: RepositoryEditedEvent,\n): Promise<AnalyzeWebhookEventResult> {\n if (event.changes.default_branch) {\n const oldUrlPrefix = `${event.repository.html_url}/blob/${event.changes.default_branch.from}`;\n const newUrlPrefix = `${event.repository.html_url}/blob/${event.repository.default_branch}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n }\n\n const changes = Object.keys(event.changes)\n .map(c => `'${c}'`)\n .join(', ');\n return {\n result: 'ignored',\n reason: `GitHub repository edited event not handled: [${changes}]`,\n };\n}\n\nasync function onRepositoryTransferredEvent(\n event: RepositoryTransferredEvent,\n): Promise<AnalyzeWebhookEventResult> {\n // TODO(freben): The web interface allows renaming while transferring, but\n // we do not yet have a way of detecting that because weirdly, the actual\n // webhooks that then get sent are\n //\n // - First a transferred event OLD_OWNER -> NEW_OWNER/NEW_REPO_NAME\n // (no mention of the OLD_REPO_NAME anywhere in the event payload)\n //\n // - Then a renamed event for NEW_OWNER of OLD_REPO_NAME -> NEW_REPO_NAME\n // (where the former of course never existed as far as we know;\n // no mention of the OLD_OWNER anywhere in the event payload)\n //\n // Handling this seems to need some form of state handling across events.\n //\n // Therefore, this code expects that OLD_REPO_NAME === NEW_REPO_NAME\n // and assumes that we did not perform a rename as part of the transfer.\n\n // The documentation states that there is an organization property in from,\n // but the types disagree. Hence the cast.\n // https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=transferred#repository\n const from = event.changes.owner.from as {\n user?: User;\n organization?: Organization;\n };\n\n let oldOwnerUrl = from.user?.html_url ?? from.organization?.html_url;\n if (!oldOwnerUrl) {\n // Some servers do not supply an html_url field, so we\n // construct it by removing the last two segments of the\n // target URL instead (which we know ends with \"/owner/repo\")\n const base = event.repository.html_url.split('/').slice(0, -2).join('/');\n oldOwnerUrl = `${base}/${from.user?.login ?? from.organization?.login}`;\n }\n\n const oldUrlPrefix = `${oldOwnerUrl}/${event.repository.name}`;\n const newUrlPrefix = `${event.repository.html_url}`;\n return {\n result: 'ok',\n events: [\n {\n type: 'repository.moved',\n fromUrl: oldUrlPrefix,\n toUrl: newUrlPrefix,\n },\n ],\n };\n}\n\n// #endregion\n"],"names":["InputError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"annotation.cjs.js","sources":["../../src/lib/annotation.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The value of this annotation is the so-called login that identifies a user on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. It is on the format\n * `<username>`, and is the same as can be seen in the URL location bar of the\n * browser when viewing that user.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_USER_LOGIN = 'github.com/user-login';\n\n/**\n * The value of this annotation is the so-called slug that identifies a team on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. It is on the format\n * `<organization>/<team>`, and is the same as can be seen in the URL location\n * bar of the browser when viewing that team.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_TEAM_SLUG = 'github.com/team-slug';\n\n/**\n * The value of this annotation is the unique identifier for a user on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. This is a stable\n * identifier that persists even if the user changes their login name.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_USER_ID = 'github.com/user-id';\n"],"names":[],"mappings":";;AAyBO,MAAM,4BAAA,GAA+B;AAWrC,MAAM,2BAAA,GAA8B;AAUpC,MAAM,yBAAA,GAA4B;;;;;;"}
1
+ {"version":3,"file":"annotation.cjs.js","sources":["../../src/lib/annotation.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The value of this annotation is the so-called login that identifies a user on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. It is on the format\n * `<username>`, and is the same as can be seen in the URL location bar of the\n * browser when viewing that user.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_USER_LOGIN = 'github.com/user-login';\n\n/**\n * The value of this annotation is the so-called slug that identifies a team on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. It is on the format\n * `<organization>/<team>`, and is the same as can be seen in the URL location\n * bar of the browser when viewing that team.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_TEAM_SLUG = 'github.com/team-slug';\n\n/**\n * The value of this annotation is the unique identifier for a user on\n * [GitHub](https://github.com) (either the public one, or a private GitHub\n * Enterprise installation) that is related to this entity. This is a stable\n * identifier that persists even if the user changes their login name.\n *\n * @public\n */\nexport const ANNOTATION_GITHUB_USER_ID = 'github.com/user-id';\n"],"names":[],"mappings":";;AAyBO,MAAM,4BAAA,GAA+B;AAWrC,MAAM,2BAAA,GAA8B;AAUpC,MAAM,yBAAA,GAA4B;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs.js","sources":["../../src/lib/config.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\n\n/**\n * The configuration parameters for a multi-org GitHub processor.\n * @public\n */\nexport type GithubMultiOrgConfig = Array<{\n /**\n * The name of the GitHub org to process.\n */\n name: string;\n /**\n * The namespace of the group created for this org.\n */\n groupNamespace: string;\n /**\n * The namespace of the users created for this org. If not specified defaults to undefined.\n */\n userNamespace: string | undefined;\n}>;\n\nexport function readGithubMultiOrgConfig(config: Config): GithubMultiOrgConfig {\n const orgConfigs = config.getOptionalConfigArray('orgs') ?? [];\n return orgConfigs.map(c => ({\n name: c.getString('name'),\n groupNamespace: (\n c.getOptionalString('groupNamespace') ?? c.getString('name')\n ).toLowerCase(),\n userNamespace: c.getOptionalString('userNamespace') ?? undefined,\n }));\n}\n"],"names":[],"mappings":";;AAqCO,SAAS,yBAAyB,MAAA,EAAsC;AAC7E,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,sBAAA,CAAuB,MAAM,KAAK,EAAC;AAC7D,EAAA,OAAO,UAAA,CAAW,IAAI,CAAA,CAAA,MAAM;AAAA,IAC1B,IAAA,EAAM,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA;AAAA,IACxB,cAAA,EAAA,CACE,EAAE,iBAAA,CAAkB,gBAAgB,KAAK,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA,EAC3D,WAAA,EAAY;AAAA,IACd,aAAA,EAAe,CAAA,CAAE,iBAAA,CAAkB,eAAe,CAAA,IAAK;AAAA,GACzD,CAAE,CAAA;AACJ;;;;"}
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/lib/config.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\n\n/**\n * The configuration parameters for a multi-org GitHub processor.\n * @public\n */\nexport type GithubMultiOrgConfig = Array<{\n /**\n * The name of the GitHub org to process.\n */\n name: string;\n /**\n * The namespace of the group created for this org.\n */\n groupNamespace: string;\n /**\n * The namespace of the users created for this org. If not specified defaults to undefined.\n */\n userNamespace: string | undefined;\n}>;\n\nexport function readGithubMultiOrgConfig(config: Config): GithubMultiOrgConfig {\n const orgConfigs = config.getOptionalConfigArray('orgs') ?? [];\n return orgConfigs.map(c => ({\n name: c.getString('name'),\n groupNamespace: (\n c.getOptionalString('groupNamespace') ?? c.getString('name')\n ).toLowerCase(),\n userNamespace: c.getOptionalString('userNamespace') ?? undefined,\n }));\n}\n"],"names":[],"mappings":";;AAqCO,SAAS,yBAAyB,MAAA,EAAsC;AAC7E,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,sBAAA,CAAuB,MAAM,KAAK,EAAC;AAC7D,EAAA,OAAO,UAAA,CAAW,IAAI,CAAA,CAAA,MAAM;AAAA,IAC1B,IAAA,EAAM,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA;AAAA,IACxB,cAAA,EAAA,CACE,EAAE,iBAAA,CAAkB,gBAAgB,KAAK,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA,EAC3D,WAAA,EAAY;AAAA,IACd,aAAA,EAAe,CAAA,CAAE,iBAAA,CAAkB,eAAe,CAAA,IAAK;AAAA,GACzD,CAAE,CAAA;AACJ;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"defaultTransformers.cjs.js","sources":["../../src/lib/defaultTransformers.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';\nimport { graphql } from '@octokit/graphql';\nimport {\n ANNOTATION_GITHUB_TEAM_SLUG,\n ANNOTATION_GITHUB_USER_LOGIN,\n ANNOTATION_GITHUB_USER_ID,\n} from './annotation';\nimport { GithubTeam, GithubUser } from './github';\n\n/**\n * Context passed to Transformers\n *\n * @public\n */\nexport interface TransformerContext {\n client: typeof graphql;\n query: string;\n org: string;\n}\n\n/**\n * Transformer for GitHub users to an Entity\n *\n * @public\n */\nexport type UserTransformer = (\n item: GithubUser,\n ctx: TransformerContext,\n) => Promise<Entity | undefined>;\n\n/**\n * Transformer for GitHub Team to an Entity\n *\n * @public\n */\nexport type TeamTransformer = (\n item: GithubTeam,\n ctx: TransformerContext,\n) => Promise<Entity | undefined>;\n\n/**\n * Options for {@link buildDefaultUserTransformer}.\n *\n * @public\n */\nexport interface DefaultUserTransformerOptions {\n /**\n * Whether to prefer organization verified domain emails over the user's\n * public GitHub email. When enabled, the transformer uses the first\n * verified domain email (with plus-addressed routing tags stripped) and\n * falls back to the public email if none are available.\n *\n * @defaultValue false\n */\n useVerifiedEmails?: boolean;\n}\n\n/**\n * Builds a user transformer with configurable email behavior.\n *\n * @public\n */\nexport function buildDefaultUserTransformer(\n options?: DefaultUserTransformerOptions,\n): UserTransformer {\n return async (item, _ctx) => {\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name: item.login,\n annotations: {\n [ANNOTATION_GITHUB_USER_LOGIN]: item.login,\n ...(item.id && { [ANNOTATION_GITHUB_USER_ID]: item.id }),\n },\n },\n spec: {\n profile: {},\n memberOf: [],\n },\n };\n\n if (item.bio) entity.metadata.description = item.bio;\n if (item.name) entity.spec.profile!.displayName = item.name;\n\n if (options?.useVerifiedEmails) {\n // GitHub returns verified domain emails as plus-addressed routing aliases\n // (e.g. user+abc123@example.com). Strip the tag to get the real address.\n const email = item.organizationVerifiedDomainEmails?.length\n ? item.organizationVerifiedDomainEmails[0].replace(/\\+[^@]*/, '')\n : item.email;\n if (email) entity.spec.profile!.email = email;\n } else {\n if (item.email) entity.spec.profile!.email = item.email;\n }\n\n if (item.avatarUrl) entity.spec.profile!.picture = item.avatarUrl;\n return entity;\n };\n}\n\n/**\n * Default transformer for GitHub users to UserEntity\n *\n * @public\n */\nexport const defaultUserTransformer: UserTransformer =\n buildDefaultUserTransformer();\n\n/**\n * Default transformer for GitHub Team to GroupEntity\n *\n * @public\n */\nexport const defaultOrganizationTeamTransformer: TeamTransformer =\n async team => {\n const annotations: { [annotationName: string]: string } = {\n [ANNOTATION_GITHUB_TEAM_SLUG]: team.combinedSlug,\n };\n\n if (team.editTeamUrl) {\n annotations['backstage.io/edit-url'] = team.editTeamUrl;\n }\n\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: team.slug,\n annotations,\n },\n spec: {\n type: 'team',\n profile: {},\n children: [],\n },\n };\n\n if (team.description) {\n entity.metadata.description = team.description;\n }\n if (team.name) {\n entity.spec.profile!.displayName = team.name;\n }\n if (team.avatarUrl) {\n entity.spec.profile!.picture = team.avatarUrl;\n }\n if (team.parentTeam) {\n entity.spec.parent = team.parentTeam.slug;\n }\n\n entity.spec.members = team.members.map(user => user.login);\n\n return entity;\n };\n"],"names":["ANNOTATION_GITHUB_USER_LOGIN","ANNOTATION_GITHUB_USER_ID","ANNOTATION_GITHUB_TEAM_SLUG"],"mappings":"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"}
1
+ {"version":3,"file":"defaultTransformers.cjs.js","sources":["../../src/lib/defaultTransformers.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';\nimport { graphql } from '@octokit/graphql';\nimport {\n ANNOTATION_GITHUB_TEAM_SLUG,\n ANNOTATION_GITHUB_USER_LOGIN,\n ANNOTATION_GITHUB_USER_ID,\n} from './annotation';\nimport { GithubTeam, GithubUser } from './github';\n\n/**\n * Context passed to Transformers\n *\n * @public\n */\nexport interface TransformerContext {\n client: typeof graphql;\n query: string;\n org: string;\n}\n\n/**\n * Transformer for GitHub users to an Entity\n *\n * @public\n */\nexport type UserTransformer = (\n item: GithubUser,\n ctx: TransformerContext,\n) => Promise<Entity | undefined>;\n\n/**\n * Transformer for GitHub Team to an Entity\n *\n * @public\n */\nexport type TeamTransformer = (\n item: GithubTeam,\n ctx: TransformerContext,\n) => Promise<Entity | undefined>;\n\n/**\n * Options for {@link buildDefaultUserTransformer}.\n *\n * @public\n */\nexport interface DefaultUserTransformerOptions {\n /**\n * Whether to prefer organization verified domain emails over the user's\n * public GitHub email. When enabled, the transformer uses the first\n * verified domain email (with plus-addressed routing tags stripped) and\n * falls back to the public email if none are available.\n *\n * @defaultValue false\n */\n useVerifiedEmails?: boolean;\n}\n\n/**\n * Builds a user transformer with configurable email behavior.\n *\n * @public\n */\nexport function buildDefaultUserTransformer(\n options?: DefaultUserTransformerOptions,\n): UserTransformer {\n return async (item, _ctx) => {\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name: item.login,\n annotations: {\n [ANNOTATION_GITHUB_USER_LOGIN]: item.login,\n ...(item.id && { [ANNOTATION_GITHUB_USER_ID]: item.id }),\n },\n },\n spec: {\n profile: {},\n memberOf: [],\n },\n };\n\n if (item.bio) entity.metadata.description = item.bio;\n if (item.name) entity.spec.profile!.displayName = item.name;\n\n if (options?.useVerifiedEmails) {\n // GitHub returns verified domain emails as plus-addressed routing aliases\n // (e.g. user+abc123@example.com). Strip the tag to get the real address.\n const email = item.organizationVerifiedDomainEmails?.length\n ? item.organizationVerifiedDomainEmails[0].replace(/\\+[^@]*/, '')\n : item.email;\n if (email) entity.spec.profile!.email = email;\n } else {\n if (item.email) entity.spec.profile!.email = item.email;\n }\n\n if (item.avatarUrl) entity.spec.profile!.picture = item.avatarUrl;\n return entity;\n };\n}\n\n/**\n * Default transformer for GitHub users to UserEntity\n *\n * @public\n */\nexport const defaultUserTransformer: UserTransformer =\n buildDefaultUserTransformer();\n\n/**\n * Default transformer for GitHub Team to GroupEntity\n *\n * @public\n */\nexport const defaultOrganizationTeamTransformer: TeamTransformer =\n async team => {\n const annotations: { [annotationName: string]: string } = {\n [ANNOTATION_GITHUB_TEAM_SLUG]: team.combinedSlug,\n };\n\n if (team.editTeamUrl) {\n annotations['backstage.io/edit-url'] = team.editTeamUrl;\n }\n\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: team.slug,\n annotations,\n },\n spec: {\n type: 'team',\n profile: {},\n children: [],\n },\n };\n\n if (team.description) {\n entity.metadata.description = team.description;\n }\n if (team.name) {\n entity.spec.profile!.displayName = team.name;\n }\n if (team.avatarUrl) {\n entity.spec.profile!.picture = team.avatarUrl;\n }\n if (team.parentTeam) {\n entity.spec.parent = team.parentTeam.slug;\n }\n\n entity.spec.members = team.members.map(user => user.login);\n\n return entity;\n };\n"],"names":["ANNOTATION_GITHUB_USER_LOGIN","ANNOTATION_GITHUB_USER_ID","ANNOTATION_GITHUB_TEAM_SLUG"],"mappings":"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"}