@red-hat-developer-hub/backstage-plugin-bulk-import-backend 5.0.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 (56) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +161 -0
  3. package/app-config.yaml +0 -0
  4. package/config.d.ts +17 -0
  5. package/dist/catalog/catalogHttpClient.cjs.js +284 -0
  6. package/dist/catalog/catalogHttpClient.cjs.js.map +1 -0
  7. package/dist/catalog/catalogInfoGenerator.cjs.js +60 -0
  8. package/dist/catalog/catalogInfoGenerator.cjs.js.map +1 -0
  9. package/dist/catalog/catalogUtils.cjs.js +34 -0
  10. package/dist/catalog/catalogUtils.cjs.js.map +1 -0
  11. package/dist/generated/openapidocument.cjs.js +1168 -0
  12. package/dist/generated/openapidocument.cjs.js.map +1 -0
  13. package/dist/github/GithubAppManager.cjs.js +395 -0
  14. package/dist/github/GithubAppManager.cjs.js.map +1 -0
  15. package/dist/github/githubApiService.cjs.js +659 -0
  16. package/dist/github/githubApiService.cjs.js.map +1 -0
  17. package/dist/github/types.cjs.js +8 -0
  18. package/dist/github/types.cjs.js.map +1 -0
  19. package/dist/github/utils/ghUtils.cjs.js +103 -0
  20. package/dist/github/utils/ghUtils.cjs.js.map +1 -0
  21. package/dist/github/utils/orgUtils.cjs.js +151 -0
  22. package/dist/github/utils/orgUtils.cjs.js.map +1 -0
  23. package/dist/github/utils/prUtils.cjs.js +85 -0
  24. package/dist/github/utils/prUtils.cjs.js.map +1 -0
  25. package/dist/github/utils/repoUtils.cjs.js +307 -0
  26. package/dist/github/utils/repoUtils.cjs.js.map +1 -0
  27. package/dist/github/utils/utils.cjs.js +206 -0
  28. package/dist/github/utils/utils.cjs.js.map +1 -0
  29. package/dist/helpers/auditLogUtils.cjs.js +112 -0
  30. package/dist/helpers/auditLogUtils.cjs.js.map +1 -0
  31. package/dist/helpers/auth.cjs.js +36 -0
  32. package/dist/helpers/auth.cjs.js.map +1 -0
  33. package/dist/helpers/loggingUtils.cjs.js +19 -0
  34. package/dist/helpers/loggingUtils.cjs.js.map +1 -0
  35. package/dist/helpers/pagination.cjs.js +19 -0
  36. package/dist/helpers/pagination.cjs.js.map +1 -0
  37. package/dist/index.cjs.js +12 -0
  38. package/dist/index.cjs.js.map +1 -0
  39. package/dist/index.d.ts +25 -0
  40. package/dist/plugin.cjs.js +54 -0
  41. package/dist/plugin.cjs.js.map +1 -0
  42. package/dist/service/handlers/handlers.cjs.js +8 -0
  43. package/dist/service/handlers/handlers.cjs.js.map +1 -0
  44. package/dist/service/handlers/import/bulkImports.cjs.js +551 -0
  45. package/dist/service/handlers/import/bulkImports.cjs.js.map +1 -0
  46. package/dist/service/handlers/import/importStatus.cjs.js +49 -0
  47. package/dist/service/handlers/import/importStatus.cjs.js.map +1 -0
  48. package/dist/service/handlers/organization/organizations.cjs.js +77 -0
  49. package/dist/service/handlers/organization/organizations.cjs.js.map +1 -0
  50. package/dist/service/handlers/ping/ping.cjs.js +12 -0
  51. package/dist/service/handlers/ping/ping.cjs.js.map +1 -0
  52. package/dist/service/handlers/repository/repositories.cjs.js +99 -0
  53. package/dist/service/handlers/repository/repositories.cjs.js.map +1 -0
  54. package/dist/service/router.cjs.js +324 -0
  55. package/dist/service/router.cjs.js.map +1 -0
  56. package/package.json +110 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"githubApiService.cjs.js","sources":["../../src/github/githubApiService.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type {\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport {\n GithubIntegrationConfig,\n ScmIntegrations,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\nimport gitUrlParse from 'git-url-parse';\n\nimport { getBranchName, getCatalogFilename } from '../catalog/catalogUtils';\nimport { logErrorIfNeeded } from '../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n} from '../service/handlers/handlers';\nimport { CustomGithubCredentialsProvider } from './GithubAppManager';\nimport {\n isGithubAppCredential,\n type ExtendedGithubCredentials,\n type GithubFetchError,\n type GithubOrganization,\n type GithubOrganizationResponse,\n type GithubRepository,\n type GithubRepositoryResponse,\n} from './types';\nimport { buildOcto } from './utils/ghUtils';\nimport {\n addGithubAppOrgs,\n addGithubTokenOrgs,\n getAllAppOrgs,\n} from './utils/orgUtils';\nimport { closePRWithComment, findOpenPRForBranch } from './utils/prUtils';\nimport {\n addGithubAppRepositories,\n addGithubTokenOrgRepositories,\n addGithubTokenRepositories,\n createOrUpdateFileInBranch,\n fileExistsInDefaultBranch,\n type ValidatedRepo,\n} from './utils/repoUtils';\nimport {\n computeTotalCount,\n executeFunctionOnFirstSuccessfulIntegration,\n extractLocationOwnerMap,\n fetchFromAllIntegrations,\n getCredentialsForConfig,\n} from './utils/utils';\n\nexport class GithubApiService {\n private readonly logger: LoggerService;\n private readonly integrations: ScmIntegrations;\n private readonly githubCredentialsProvider: CustomGithubCredentialsProvider;\n private readonly config: Config;\n // Cache for storing ETags (used for efficient caching of unchanged data returned by GitHub)\n private readonly cache: CacheService;\n\n constructor(\n logger: LoggerService,\n config: Config,\n cacheService: CacheService,\n ) {\n this.logger = logger;\n this.config = config;\n this.integrations = ScmIntegrations.fromConfig(config);\n this.githubCredentialsProvider =\n CustomGithubCredentialsProvider.fromIntegrations(this.integrations);\n this.cache = cacheService;\n }\n\n async getRepositoryFromIntegrations(repoUrl: string): Promise<{\n repository?: GithubRepository;\n errors?: GithubFetchError[];\n }> {\n const gitUrl = gitUrlParse(repoUrl);\n\n const ghConfig = this.integrations.github.byUrl(repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(\n `No GitHub integration config found for repo ${repoUrl}. Please add a configuration entry under 'integrations.github`,\n );\n }\n\n const credentials = await getCredentialsForConfig(\n this.githubCredentialsProvider,\n ghConfig,\n );\n const errors = new Map<number, GithubFetchError>();\n let repository: GithubRepository | undefined = undefined;\n for (const credential of credentials) {\n const octokit = buildOcto(\n {\n logger: this.logger,\n cache: this.cache,\n },\n { credential, errors, owner: gitUrl.owner },\n ghConfig.apiBaseUrl,\n );\n if (!octokit) {\n continue;\n }\n const resp = await octokit.rest.repos.get({\n owner: gitUrl.owner,\n repo: gitUrl.name,\n });\n const repo = resp?.data;\n if (!repo) {\n continue;\n }\n repository = {\n name: repo.name,\n full_name: repo.full_name,\n url: repo.url,\n html_url: repo.html_url,\n default_branch: repo.default_branch,\n updated_at: repo.updated_at,\n };\n break;\n }\n\n return {\n repository,\n errors: Array.from(errors.values()),\n };\n }\n\n async getOrganizationsFromIntegrations(\n search?: string,\n pageNumber: number = DefaultPageNumber,\n pageSize: number = DefaultPageSize,\n ): Promise<GithubOrganizationResponse> {\n const orgs = new Map<string, GithubOrganization>();\n const result = await fetchFromAllIntegrations(\n {\n logger: this.logger,\n cache: this.cache,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n dataFetcher: async (\n octokit: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => {\n const dataFetchErrors = new Map<number, GithubFetchError>();\n const resp = isGithubAppCredential(credential)\n ? await addGithubAppOrgs(\n {\n logger: this.logger,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n octokit,\n ghConfig,\n {\n credentialAccountLogin: credential.accountLogin,\n search,\n orgs,\n errors: dataFetchErrors,\n },\n )\n : await addGithubTokenOrgs(\n {\n logger: this.logger,\n },\n octokit,\n credential,\n {\n search,\n orgs,\n pageNumber,\n pageSize,\n errors: dataFetchErrors,\n },\n );\n\n this.logger.debug(\n `Got ${resp.totalCount} org(s) for ${ghConfig.host}`,\n );\n return {\n result: resp.totalCount ?? 0,\n errors: Array.from(dataFetchErrors.values()),\n };\n },\n },\n );\n\n const orgList = Array.from(orgs.values());\n const totalCount = computeTotalCount(orgList, result.data, pageSize);\n return {\n organizations: orgList,\n errors: Array.from(result.errors?.values() ?? []),\n totalCount,\n };\n }\n\n async getOrgRepositoriesFromIntegrations(\n orgName: string,\n search?: string,\n pageNumber: number = DefaultPageNumber,\n pageSize: number = DefaultPageSize,\n ): Promise<GithubRepositoryResponse> {\n const repositories = new Map<string, GithubRepository>();\n const result = await fetchFromAllIntegrations(\n {\n logger: this.logger,\n cache: this.cache,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n dataFetcher: async (\n octokit: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => {\n const dataFetchErrors = new Map<number, GithubFetchError>();\n let resp: { totalCount?: number };\n if (isGithubAppCredential(credential)) {\n if (credential.accountLogin !== orgName) {\n return {};\n }\n resp = await addGithubAppRepositories(\n {\n logger: this.logger,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n octokit,\n credential,\n ghConfig,\n repositories,\n dataFetchErrors,\n {\n search,\n pageNumber,\n pageSize,\n },\n );\n } else {\n resp = await addGithubTokenOrgRepositories(\n {\n logger: this.logger,\n },\n octokit,\n credential,\n orgName,\n repositories,\n dataFetchErrors,\n {\n search,\n pageNumber,\n pageSize,\n },\n );\n }\n this.logger.debug(\n `Got ${resp.totalCount} org repo(s) for ${ghConfig.host}`,\n );\n return {\n stopFetchingData: true,\n result: resp.totalCount ?? 0,\n errors: Array.from(dataFetchErrors.values()),\n };\n },\n },\n );\n\n const repoList = Array.from(repositories.values());\n const totalCount = computeTotalCount(repoList, result.data, pageSize);\n return {\n repositories: repoList,\n errors: Array.from(result.errors?.values() ?? []),\n totalCount,\n };\n }\n\n /**\n * Returns GithubRepositoryResponse containing:\n * - a list of unique repositories the github integrations have access to\n * - a list of errors encountered by each app and/or token (if any exist)\n */\n async getRepositoriesFromIntegrations(\n search?: string,\n pageNumber: number = DefaultPageNumber,\n pageSize: number = DefaultPageSize,\n ): Promise<GithubRepositoryResponse> {\n const repositories = new Map<string, GithubRepository>();\n const result = await fetchFromAllIntegrations(\n {\n logger: this.logger,\n cache: this.cache,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n dataFetcher: async (\n octokit: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => {\n const dataFetchErrors = new Map<number, GithubFetchError>();\n const resp = isGithubAppCredential(credential)\n ? await addGithubAppRepositories(\n {\n logger: this.logger,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n octokit,\n credential,\n ghConfig,\n repositories,\n dataFetchErrors,\n {\n search,\n pageNumber,\n pageSize,\n },\n )\n : await addGithubTokenRepositories(\n {\n logger: this.logger,\n },\n octokit,\n credential,\n repositories,\n dataFetchErrors,\n {\n search,\n pageNumber,\n pageSize,\n },\n );\n this.logger.debug(\n `Got ${resp.totalCount} repo(s) for ${ghConfig.host}`,\n );\n return {\n result: resp.totalCount ?? 0,\n errors: Array.from(dataFetchErrors.values()),\n };\n },\n },\n );\n\n const repoList = Array.from(repositories.values());\n const totalCount = computeTotalCount(repoList, result.data, pageSize);\n return {\n repositories: repoList,\n errors: Array.from(result.errors?.values() ?? []),\n totalCount,\n };\n }\n\n async filterLocationsAccessibleFromIntegrations(\n locationUrls: string[],\n ): Promise<string[]> {\n const locationGitOwnerMap = extractLocationOwnerMap(locationUrls);\n\n const allAccessibleAppOrgs = new Set<string>();\n const allAccessibleTokenOrgs = new Set<string>();\n const allAccessibleUsernames = new Set<string>();\n await fetchFromAllIntegrations(\n {\n logger: this.logger,\n cache: this.cache,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n dataFetcher: async (\n octokit: Octokit,\n credential: ExtendedGithubCredentials,\n ghConfig: GithubIntegrationConfig,\n ) => {\n if (isGithubAppCredential(credential)) {\n const appOrgMap = await getAllAppOrgs(\n this.githubCredentialsProvider,\n ghConfig,\n credential.accountLogin,\n );\n for (const [_, ghOrg] of appOrgMap) {\n allAccessibleAppOrgs.add(ghOrg.name);\n }\n } else {\n // find authenticated GitHub owner...\n const username = (await octokit.rest.users.getAuthenticated())?.data\n ?.login;\n if (username) {\n allAccessibleUsernames.add(username);\n }\n // ... along with orgs accessible from the token auth\n (await octokit.paginate(octokit.rest.orgs.listForAuthenticatedUser))\n ?.map(org => org.login)\n ?.forEach(orgName => allAccessibleTokenOrgs.add(orgName));\n }\n return {};\n },\n },\n );\n\n return locationUrls.filter(loc => {\n if (!locationGitOwnerMap.has(loc)) {\n return false;\n }\n const owner = locationGitOwnerMap.get(loc)!;\n return (\n allAccessibleAppOrgs.has(owner) ||\n allAccessibleTokenOrgs.has(owner) ||\n allAccessibleUsernames.has(owner)\n );\n });\n }\n\n async findImportOpenPr(\n logger: LoggerService,\n input: {\n repoUrl: string;\n includeCatalogInfoContent?: boolean;\n },\n ): Promise<{\n prNum?: number;\n prUrl?: string;\n prTitle?: string;\n prBody?: string;\n prCatalogInfoContent?: string;\n lastUpdate?: string;\n }> {\n const ghConfig = this.integrations.github.byUrl(input.repoUrl)?.config;\n if (!ghConfig) {\n throw new Error(`Could not find GH integration from ${input.repoUrl}`);\n }\n\n const gitUrl = gitUrlParse(input.repoUrl);\n const owner = gitUrl.organization;\n const repo = gitUrl.name;\n\n const credentials = await this.githubCredentialsProvider.getAllCredentials({\n host: ghConfig.host,\n });\n if (credentials.length === 0) {\n throw new Error(`No credentials for GH integration`);\n }\n\n const branchName = getBranchName(this.config);\n for (const credential of credentials) {\n const octo = buildOcto(\n {\n logger: this.logger,\n cache: this.cache,\n },\n { credential, owner },\n ghConfig.apiBaseUrl,\n );\n if (!octo) {\n continue;\n }\n try {\n return await findOpenPRForBranch(\n logger,\n this.config,\n octo,\n owner,\n repo,\n branchName,\n input.includeCatalogInfoContent,\n );\n } catch (error: any) {\n logErrorIfNeeded(this.logger, 'Error fetching pull requests', error);\n }\n }\n return {};\n }\n\n async submitPrToRepo(\n logger: LoggerService,\n input: {\n repoUrl: string;\n gitUrl: gitUrlParse.GitUrl;\n defaultBranch?: string;\n prTitle: string;\n prBody: string;\n catalogInfoContent: string;\n },\n ): Promise<{\n prUrl?: string;\n prNumber?: number;\n hasChanges?: boolean;\n lastUpdate?: string;\n errors?: string[];\n }> {\n const fileName = getCatalogFilename(this.config);\n const errors: any[] = [];\n\n const result = await executeFunctionOnFirstSuccessfulIntegration(\n {\n logger: this.logger,\n cache: this.cache,\n config: this.config,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n repoUrl: input.repoUrl,\n fn: async (\n validatedRepo: ValidatedRepo,\n octo: Octokit,\n ): Promise<{\n successful: boolean;\n result?: {\n prUrl?: string;\n prNumber?: number;\n hasChanges?: boolean;\n lastUpdate?: string;\n };\n }> => {\n const { owner, repo, branchName } = validatedRepo;\n try {\n // Check if there is already a catalogInfo in the default branch\n const catalogInfoFileExists = await fileExistsInDefaultBranch(\n logger,\n octo,\n owner,\n repo,\n fileName,\n input.defaultBranch,\n );\n if (catalogInfoFileExists) {\n // No need to create a PR => component will be imported as is\n return {\n successful: true,\n result: {\n hasChanges: false,\n },\n };\n }\n\n const existingPrForBranch = await findOpenPRForBranch(\n logger,\n this.config,\n octo,\n owner,\n repo,\n branchName,\n );\n\n const repoData = await octo.rest.repos.get({\n owner,\n repo,\n });\n const parentRef = await octo.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${repoData.data.default_branch}`,\n });\n if (existingPrForBranch.prNum) {\n await createOrUpdateFileInBranch(\n octo,\n owner,\n repo,\n branchName,\n fileName,\n input.catalogInfoContent,\n );\n const pullRequestResponse = await octo.rest.pulls.update({\n owner,\n repo,\n pull_number: existingPrForBranch.prNum,\n title: input.prTitle,\n body: input.prBody,\n head: branchName,\n base: repoData.data.default_branch,\n });\n return {\n successful: true,\n result: {\n prNumber: existingPrForBranch.prNum,\n prUrl: pullRequestResponse.data.html_url,\n lastUpdate: pullRequestResponse.data.updated_at,\n },\n };\n }\n\n let branchExists = false;\n try {\n await octo.rest.git.getRef({\n owner,\n repo,\n ref: `heads/${branchName}`,\n });\n branchExists = true;\n } catch (error: any) {\n if (error.status === 404) {\n await octo.rest.git.createRef({\n owner,\n repo,\n ref: `refs/heads/${branchName}`,\n sha: parentRef.data.object.sha,\n });\n } else {\n throw error;\n }\n }\n\n if (branchExists) {\n // update it in case it is outdated compared to the base branch\n try {\n await octo.repos.merge({\n owner: owner,\n repo: repo,\n base: branchName,\n head: repoData.data.default_branch,\n });\n } catch (error: any) {\n logErrorIfNeeded(\n this.logger,\n `Could not merge default branch ${repoData.data.default_branch} into import branch ${branchName}`,\n error,\n );\n }\n }\n\n await createOrUpdateFileInBranch(\n octo,\n owner,\n repo,\n branchName,\n fileName,\n input.catalogInfoContent,\n );\n\n const pullRequestResponse = await octo.rest.pulls.create({\n owner,\n repo,\n title: input.prTitle,\n body: input.prBody,\n head: branchName,\n base: repoData.data.default_branch,\n });\n return {\n successful: true,\n result: {\n prNumber: pullRequestResponse.data.number,\n prUrl: pullRequestResponse.data.html_url,\n lastUpdate: pullRequestResponse.data.updated_at,\n hasChanges: true,\n },\n };\n } catch (e: any) {\n logErrorIfNeeded(\n this.logger,\n `Couldn't create PR in ${input.repoUrl}`,\n e,\n );\n errors.push(e.message);\n return { successful: false };\n }\n },\n },\n );\n\n if (result) {\n return result;\n }\n\n logger.warn(\n `Tried all possible GitHub credentials, but could not create PR in ${input.repoUrl}. Please try again later...`,\n );\n\n return {\n errors: errors,\n };\n }\n\n async hasFileInRepo(input: {\n repoUrl: string;\n defaultBranch?: string;\n fileName: string;\n }) {\n const fileExists = await executeFunctionOnFirstSuccessfulIntegration(\n {\n logger: this.logger,\n cache: this.cache,\n config: this.config,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n repoUrl: input.repoUrl,\n fn: async (validatedRepo: ValidatedRepo, octo: Octokit) => {\n const { owner, repo } = validatedRepo;\n const exists = await fileExistsInDefaultBranch(\n this.logger,\n octo,\n owner,\n repo,\n input.fileName,\n input.defaultBranch,\n );\n if (exists === undefined) {\n return { successful: false };\n }\n return { successful: true, result: exists };\n },\n },\n );\n\n if (fileExists === undefined) {\n throw new Error(\n `Could not determine if repo at ${input.repoUrl} already has a file named ${input.fileName} in its default branch (${input.defaultBranch})`,\n );\n }\n\n return fileExists;\n }\n\n async closeImportPR(\n logger: LoggerService,\n input: {\n repoUrl: string;\n gitUrl: gitUrlParse.GitUrl;\n comment: string;\n },\n ) {\n await executeFunctionOnFirstSuccessfulIntegration(\n {\n logger: this.logger,\n cache: this.cache,\n config: this.config,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n repoUrl: input.repoUrl,\n fn: async (validatedRepo: ValidatedRepo, octo: Octokit) => {\n const { owner, repo, branchName } = validatedRepo;\n try {\n const existingPrForBranch = await findOpenPRForBranch(\n logger,\n this.config,\n octo,\n owner,\n repo,\n branchName,\n );\n if (existingPrForBranch.prNum) {\n await closePRWithComment(\n octo,\n owner,\n repo,\n existingPrForBranch.prNum,\n input.comment,\n );\n }\n return { successful: true };\n } catch (e: any) {\n logErrorIfNeeded(\n this.logger,\n `Couldn't close PR in ${input.repoUrl}`,\n e,\n );\n return { successful: false };\n }\n },\n },\n );\n }\n\n async deleteImportBranch(input: {\n repoUrl: string;\n gitUrl: gitUrlParse.GitUrl;\n }) {\n await executeFunctionOnFirstSuccessfulIntegration(\n {\n logger: this.logger,\n cache: this.cache,\n config: this.config,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n repoUrl: input.repoUrl,\n fn: async (validatedRepo: ValidatedRepo, octo: Octokit) => {\n const { owner, repo, branchName } = validatedRepo;\n try {\n await octo.git.deleteRef({\n owner: owner,\n repo: repo,\n ref: `heads/${branchName}`,\n });\n return { successful: true };\n } catch (e: any) {\n logErrorIfNeeded(\n this.logger,\n `Couldn't close import PR and/or delete import branch in ${input.repoUrl}`,\n e,\n );\n return { successful: false };\n }\n },\n },\n );\n }\n\n async isRepoEmpty(input: { repoUrl: string }) {\n return await executeFunctionOnFirstSuccessfulIntegration(\n {\n logger: this.logger,\n cache: this.cache,\n config: this.config,\n githubCredentialsProvider: this.githubCredentialsProvider,\n },\n this.integrations,\n {\n repoUrl: input.repoUrl,\n fn: async (validatedRepo: ValidatedRepo, octo: Octokit) => {\n const { owner, repo } = validatedRepo;\n const resp = await octo.rest.repos.listContributors({\n owner: owner,\n repo: repo,\n page: 1,\n per_page: 1,\n });\n const status = resp.status as 200 | 204;\n return { successful: true, result: status === 204 };\n },\n },\n );\n }\n}\n"],"names":["ScmIntegrations","CustomGithubCredentialsProvider","gitUrlParse","getCredentialsForConfig","buildOcto","DefaultPageNumber","DefaultPageSize","fetchFromAllIntegrations","isGithubAppCredential","addGithubAppOrgs","addGithubTokenOrgs","computeTotalCount","addGithubAppRepositories","addGithubTokenOrgRepositories","addGithubTokenRepositories","extractLocationOwnerMap","getAllAppOrgs","getBranchName","findOpenPRForBranch","logErrorIfNeeded","getCatalogFilename","executeFunctionOnFirstSuccessfulIntegration","fileExistsInDefaultBranch","createOrUpdateFileInBranch","pullRequestResponse","closePRWithComment"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ function isGithubAppCredential(credential) {
4
+ return "appId" in credential && credential.type === "app";
5
+ }
6
+
7
+ exports.isGithubAppCredential = isGithubAppCredential;
8
+ //# sourceMappingURL=types.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs.js","sources":["../../src/github/types.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type { SerializedError } from '@backstage/errors';\nimport type {\n GithubCredentials,\n GithubCredentialsProvider,\n} from '@backstage/integration';\n\n// From https://docs.github.com/en/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations\nexport type GithubOrganization = {\n name: string;\n id: number;\n description?: string;\n url?: string;\n html_url?: string;\n repos_url?: string;\n events_url?: string;\n hooks_url?: string;\n issues_url?: string;\n members_url?: string;\n public_members_url?: string;\n avatar_url?: string;\n public_repos?: number;\n total_private_repos?: number;\n /**\n * Number of internal repositories, accessible to all members in a GH enterprise\n */\n owned_private_repos?: number;\n};\n\nexport type GithubRepository = {\n name: string;\n /**\n * The full name of the repository in the form of owner/repo\n */\n full_name: string;\n /**\n * The API url to the repository\n */\n url: string;\n /**\n * The HTML URL to the repository\n */\n html_url: string;\n /**\n * The default \"main\" branch of the repository to place the `catalog-info.yaml` file into\n */\n default_branch: string;\n /**\n * The date-time the repository was last updated at\n */\n updated_at?: string | null;\n};\n\n/**\n * The type of credentials produced by the credential provider.\n *\n * @public\n */\n\nexport type GithubFetchError =\n | {\n type: 'app';\n appId: number;\n error: SerializedError;\n }\n | {\n type: 'token';\n error: SerializedError;\n };\n\nexport type GithubOrganizationResponse = {\n organizations: GithubOrganization[];\n errors: GithubFetchError[];\n totalCount?: number;\n};\n\nexport type GithubRepositoryResponse = {\n repositories: GithubRepository[];\n errors: GithubFetchError[];\n totalCount?: number;\n};\n\nexport type AppCredentialFetchResult = AppCredential | AppCredentialError;\n\nexport type AppCredential = {\n appId: number;\n accessToken: string | undefined;\n installationAccountLogin?: string;\n};\nexport type AppCredentialError = { appId: number; error: Error };\n\nexport type ExtendedGithubCredentials =\n | GithubCredentials\n | GithubAppCredentials\n | GithubAppError;\n\nexport type GithubAppCredentials = GithubCredentials & {\n type: 'app';\n appId: number;\n accountLogin?: string;\n};\n\nexport type GithubAppError = {\n type: 'app';\n appId: number;\n error: Error;\n};\n\nexport function isGithubAppCredential(\n credential: ExtendedGithubCredentials,\n): credential is GithubAppCredentials {\n return 'appId' in credential && credential.type === 'app';\n}\n\nexport interface ExtendedGithubCredentialsProvider\n extends GithubCredentialsProvider {\n getAllCredentials: (options: {\n host: string;\n }) => Promise<ExtendedGithubCredentials[]>;\n}\n"],"names":[],"mappings":";;AA2HO,SAAS,sBACd,UACoC,EAAA;AACpC,EAAO,OAAA,OAAA,IAAW,UAAc,IAAA,UAAA,CAAW,IAAS,KAAA,KAAA,CAAA;AACtD;;;;"}
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ var rest = require('@octokit/rest');
4
+ var types = require('../types.cjs.js');
5
+ var utils = require('./utils.cjs.js');
6
+
7
+ const GITHUB_DEFAULT_API_ENDPOINT = "https://api.github.com";
8
+ const RESPONSE_CACHE_TTL_MILLIS = 60 * 60 * 1e3;
9
+ function buildOcto(deps, input, apiBaseUrl = GITHUB_DEFAULT_API_ENDPOINT) {
10
+ if ("error" in input.credential) {
11
+ if (input.credential.error?.name !== "NotFoundError") {
12
+ deps.logger.error(
13
+ `Obtaining the Access Token Github App with appId: ${input.credential.appId} failed with ${input.credential.error}`
14
+ );
15
+ const credentialError = utils.createCredentialError(input.credential);
16
+ if (credentialError) {
17
+ deps.logger.debug(`${input.credential.appId}: ${credentialError}`);
18
+ if (input.errors) {
19
+ input.errors.set(input.credential.appId, credentialError);
20
+ }
21
+ }
22
+ }
23
+ return void 0;
24
+ }
25
+ if (types.isGithubAppCredential(input.credential) && input.owner && input.credential.accountLogin !== input.owner) {
26
+ return void 0;
27
+ }
28
+ const octokit = new rest.Octokit({
29
+ baseUrl: apiBaseUrl,
30
+ auth: input.credential.token
31
+ });
32
+ registerHooks(deps, octokit);
33
+ return octokit;
34
+ }
35
+ function registerHooks(deps, octokit) {
36
+ const extractCacheKey = (options) => `${options.method}--${tryReplacingPlaceholdersInUrl(options)}`;
37
+ octokit.hook.before("request", async (options) => {
38
+ if (!options.headers) {
39
+ options.headers = {
40
+ accept: "application/json",
41
+ "user-agent": "rhdh/bulk-import"
42
+ };
43
+ }
44
+ const cacheKey = extractCacheKey(options);
45
+ const existingEtag = await deps.cache.get(cacheKey)?.then((val) => val?.etag);
46
+ if (existingEtag) {
47
+ options.headers["If-None-Match"] = existingEtag;
48
+ } else {
49
+ deps.logger.debug(`cache miss for key "${cacheKey}"`);
50
+ }
51
+ });
52
+ octokit.hook.after("request", async (response, options) => {
53
+ deps.logger.debug(
54
+ `[GH API] ${options.method} ${tryReplacingPlaceholdersInUrl(options)}: ${response.status}`
55
+ );
56
+ const cacheKey = extractCacheKey(options);
57
+ await deps.cache.set(
58
+ cacheKey,
59
+ {
60
+ etag: response.headers.etag,
61
+ ...response
62
+ },
63
+ { ttl: RESPONSE_CACHE_TTL_MILLIS }
64
+ );
65
+ });
66
+ octokit.hook.error("request", async (error, options) => {
67
+ deps.logger.debug(
68
+ `[GH API] ${options.method} ${tryReplacingPlaceholdersInUrl(options)}: ${error.status}`
69
+ );
70
+ if (error.status !== 304) {
71
+ throw error;
72
+ }
73
+ return await deps.cache.get(extractCacheKey(options));
74
+ });
75
+ }
76
+ function tryReplacingPlaceholdersInUrl(options) {
77
+ let result = "";
78
+ let startIdx = 0;
79
+ const url = options.url;
80
+ if (!url) {
81
+ return void 0;
82
+ }
83
+ while (startIdx < url.length) {
84
+ const openBraceIdx = url.indexOf("{", startIdx);
85
+ if (openBraceIdx === -1) {
86
+ result += url.slice(startIdx);
87
+ break;
88
+ }
89
+ result += url.slice(startIdx, openBraceIdx);
90
+ const closeBraceIdx = url.indexOf("}", openBraceIdx);
91
+ if (closeBraceIdx === -1) {
92
+ result += url.slice(openBraceIdx);
93
+ break;
94
+ }
95
+ const key = url.slice(openBraceIdx + 1, closeBraceIdx);
96
+ result += options[key] ?? `{${key}}`;
97
+ startIdx = closeBraceIdx + 1;
98
+ }
99
+ return result;
100
+ }
101
+
102
+ exports.buildOcto = buildOcto;
103
+ //# sourceMappingURL=ghUtils.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghUtils.cjs.js","sources":["../../../src/github/utils/ghUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type {\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\n\nimport { Octokit } from '@octokit/rest';\n\nimport {\n isGithubAppCredential,\n type ExtendedGithubCredentials,\n type GithubFetchError,\n} from '../types';\nimport { createCredentialError } from './utils';\n\nconst GITHUB_DEFAULT_API_ENDPOINT = 'https://api.github.com';\n\n// Cache TTL per entry added, based on the lower values of rate limits imposed by GH,\n// i.e., 5K requests per hour for requests using a personal token.\n// GitHub Apps owned by enterprises have a higher limit of 15K per hour.\n// See https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28\nconst RESPONSE_CACHE_TTL_MILLIS = 60 * 60 * 1000;\n\nexport function buildOcto(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n },\n input: {\n credential: ExtendedGithubCredentials;\n owner?: string;\n errors?: Map<number, GithubFetchError>;\n },\n apiBaseUrl: string = GITHUB_DEFAULT_API_ENDPOINT,\n): Octokit | undefined {\n if ('error' in input.credential) {\n if (input.credential.error?.name !== 'NotFoundError') {\n deps.logger.error(\n `Obtaining the Access Token Github App with appId: ${input.credential.appId} failed with ${input.credential.error}`,\n );\n const credentialError = createCredentialError(input.credential);\n if (credentialError) {\n deps.logger.debug(`${input.credential.appId}: ${credentialError}`);\n if (input.errors) {\n input.errors.set(input.credential.appId, credentialError);\n }\n }\n }\n return undefined;\n }\n if (\n isGithubAppCredential(input.credential) &&\n input.owner &&\n input.credential.accountLogin !== input.owner\n ) {\n return undefined;\n }\n const octokit = new Octokit({\n baseUrl: apiBaseUrl,\n auth: input.credential.token,\n });\n registerHooks(deps, octokit);\n return octokit;\n}\n\nfunction registerHooks(\n deps: {\n logger: LoggerService;\n cache: CacheService;\n },\n octokit: Octokit,\n) {\n const extractCacheKey = (options: any) =>\n `${options.method}--${tryReplacingPlaceholdersInUrl(options)}`;\n\n octokit.hook.before('request', async options => {\n if (!options.headers) {\n options.headers = {\n accept: 'application/json',\n 'user-agent': 'rhdh/bulk-import',\n };\n }\n // Use ETag from in-memory cache if available\n const cacheKey = extractCacheKey(options);\n const existingEtag = await deps.cache\n .get(cacheKey)\n ?.then((val?: any) => val?.etag);\n if (existingEtag) {\n options.headers['If-None-Match'] = existingEtag;\n } else {\n deps.logger.debug(`cache miss for key \"${cacheKey}\"`);\n }\n });\n\n octokit.hook.after('request', async (response, options) => {\n deps.logger.debug(\n `[GH API] ${options.method} ${tryReplacingPlaceholdersInUrl(options)}: ${\n response.status\n }`,\n );\n // If we get a successful response, the resource has changed, so update the in-memory cache\n const cacheKey = extractCacheKey(options);\n await deps.cache.set(\n cacheKey,\n {\n etag: response.headers.etag,\n ...response,\n },\n { ttl: RESPONSE_CACHE_TTL_MILLIS },\n );\n });\n\n octokit.hook.error('request', async (error: any, options) => {\n deps.logger.debug(\n `[GH API] ${options.method} ${tryReplacingPlaceholdersInUrl(options)}: ${\n error.status\n }`,\n );\n if (error.status !== 304) {\n throw error;\n }\n // \"304 Not Modified\" means that the resource hasn't changed,\n // and we should have a version of it in the cache\n return await deps.cache.get(extractCacheKey(options));\n });\n}\n\nfunction tryReplacingPlaceholdersInUrl(options: any) {\n // options.url might contain placeholders => need to replace them with their actual values to not get colliding keys\n\n let result = '';\n let startIdx = 0;\n\n const url = options.url as string | undefined;\n if (!url) {\n return undefined;\n }\n while (startIdx < url.length) {\n const openBraceIdx = url.indexOf('{', startIdx);\n if (openBraceIdx === -1) {\n // no '{' => append the rest of the string\n result += url.slice(startIdx);\n break;\n }\n\n // append part of the string before '{'\n result += url.slice(startIdx, openBraceIdx);\n\n const closeBraceIdx = url.indexOf('}', openBraceIdx);\n if (closeBraceIdx === -1) {\n // no '}' => append the rest of the string\n result += url.slice(openBraceIdx);\n break;\n }\n\n const key = url.slice(openBraceIdx + 1, closeBraceIdx);\n result += options[key] ?? `{${key}}`;\n\n startIdx = closeBraceIdx + 1;\n }\n\n return result;\n}\n"],"names":["createCredentialError","isGithubAppCredential","Octokit"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+
3
+ require('@backstage/errors');
4
+ require('@backstage/plugin-permission-common');
5
+ require('@red-hat-developer-hub/backstage-plugin-bulk-import-common');
6
+ var loggingUtils = require('../../helpers/loggingUtils.cjs.js');
7
+ var pagination = require('../../helpers/pagination.cjs.js');
8
+ var handlers = require('../../service/handlers/handlers.cjs.js');
9
+ var utils = require('./utils.cjs.js');
10
+
11
+ async function getAllAppOrgs(githubCredentialsProvider, ghConfig, credentialAccountLogin) {
12
+ const result = /* @__PURE__ */ new Map();
13
+ const resp = await githubCredentialsProvider.getAllAppInstallations(ghConfig);
14
+ for (const installation of resp ?? []) {
15
+ if (!(installation.account && installation.target_type?.toLowerCase() === "organization")) {
16
+ continue;
17
+ }
18
+ const acc = installation.account;
19
+ if (credentialAccountLogin !== acc.login) {
20
+ continue;
21
+ }
22
+ result.set(acc.url, {
23
+ id: acc.id,
24
+ description: acc.description ?? void 0,
25
+ name: acc.login,
26
+ url: acc.html_url,
27
+ html_url: acc.html_url,
28
+ repos_url: acc.repos_url,
29
+ events_url: acc.events_url
30
+ });
31
+ }
32
+ return result;
33
+ }
34
+ async function addGithubAppOrgs(deps, octokit, ghConfig, params) {
35
+ const credentialAccountLogin = params.credentialAccountLogin;
36
+ const search = params.search;
37
+ const orgs = params.orgs;
38
+ const errors = params.errors;
39
+ let totalCount = 0;
40
+ try {
41
+ const resp = await getAllAppOrgs(
42
+ deps.githubCredentialsProvider,
43
+ ghConfig,
44
+ credentialAccountLogin
45
+ );
46
+ for (const [orgUrl, ghOrg] of resp) {
47
+ if (search && !ghOrg.name.toLowerCase().includes(search.toLowerCase())) {
48
+ continue;
49
+ }
50
+ const orgData = await octokit.request(
51
+ orgUrl.replace("/users/", "/orgs/")
52
+ );
53
+ orgs.set(ghOrg.name, {
54
+ ...ghOrg,
55
+ public_repos: orgData?.data?.public_repos,
56
+ total_private_repos: orgData?.data?.total_private_repos,
57
+ owned_private_repos: orgData?.data?.owned_private_repos
58
+ });
59
+ totalCount++;
60
+ }
61
+ } catch (err) {
62
+ loggingUtils.logErrorIfNeeded(
63
+ deps.logger,
64
+ `Fetching organizations with access token for github app`,
65
+ err
66
+ );
67
+ errors.set(-1, err.message);
68
+ }
69
+ return { totalCount };
70
+ }
71
+ async function addGithubTokenOrgs(deps, octokit, credential, params) {
72
+ const search = params.search;
73
+ const orgs = params.orgs;
74
+ const errors = params.errors;
75
+ const pageNumber = params.pageNumber ?? handlers.DefaultPageNumber;
76
+ const pageSize = params.pageSize ?? handlers.DefaultPageSize;
77
+ let totalCount;
78
+ try {
79
+ let matchingOrgs;
80
+ if (search) {
81
+ const resp = await octokit.paginate(
82
+ octokit.rest.orgs.listForAuthenticatedUser,
83
+ {
84
+ sort: "full_name",
85
+ direction: "asc"
86
+ }
87
+ );
88
+ const allMatchingOrgs = resp?.filter(
89
+ (org) => org.login.toLowerCase().includes(search.toLowerCase())
90
+ ) ?? [];
91
+ const matchingOrgsPage = pagination.paginateArray(
92
+ allMatchingOrgs,
93
+ pageNumber,
94
+ pageSize
95
+ );
96
+ matchingOrgs = matchingOrgsPage.result;
97
+ totalCount = matchingOrgsPage.totalCount;
98
+ } else {
99
+ const resp = await octokit.rest.orgs.listForAuthenticatedUser({
100
+ page: pageNumber,
101
+ per_page: pageSize,
102
+ sort: "full_name",
103
+ direction: "asc"
104
+ });
105
+ matchingOrgs = resp?.data ?? [];
106
+ totalCount = await utils.computeTotalCountFromGitHubToken(
107
+ deps,
108
+ async (lastPageNumber) => octokit.orgs.listForAuthenticatedUser({
109
+ page: lastPageNumber,
110
+ per_page: 100
111
+ }).then((lastPageResp) => lastPageResp.data.length),
112
+ "orgs.listForAuthenticatedUser",
113
+ resp?.data?.length,
114
+ resp?.headers?.link
115
+ );
116
+ }
117
+ for (const org of matchingOrgs) {
118
+ const orgData = await octokit.request(org.url);
119
+ const ghOrg = {
120
+ id: org.id,
121
+ name: org.login,
122
+ description: org.description ?? void 0,
123
+ url: orgData?.data?.html_url ?? org.url,
124
+ repos_url: org.repos_url,
125
+ hooks_url: org.hooks_url,
126
+ issues_url: org.issues_url,
127
+ members_url: org.members_url,
128
+ public_members_url: org.public_members_url,
129
+ avatar_url: org.avatar_url,
130
+ public_repos: orgData?.data?.public_repos,
131
+ total_private_repos: orgData?.data?.total_private_repos,
132
+ owned_private_repos: orgData?.data?.owned_private_repos
133
+ };
134
+ orgs.set(org.login, ghOrg);
135
+ }
136
+ } catch (err) {
137
+ utils.handleError(
138
+ { logger: deps.logger },
139
+ "Fetching orgs with token from token",
140
+ credential,
141
+ errors,
142
+ err
143
+ );
144
+ }
145
+ return { totalCount };
146
+ }
147
+
148
+ exports.addGithubAppOrgs = addGithubAppOrgs;
149
+ exports.addGithubTokenOrgs = addGithubTokenOrgs;
150
+ exports.getAllAppOrgs = getAllAppOrgs;
151
+ //# sourceMappingURL=orgUtils.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgUtils.cjs.js","sources":["../../../src/github/utils/orgUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type {\n GithubCredentials,\n GithubIntegrationConfig,\n} from '@backstage/integration';\n\nimport { Octokit } from '@octokit/rest';\n\nimport { logErrorIfNeeded, paginateArray } from '../../helpers';\nimport {\n DefaultPageNumber,\n DefaultPageSize,\n} from '../../service/handlers/handlers';\nimport type { CustomGithubCredentialsProvider } from '../GithubAppManager';\nimport type { GithubFetchError, GithubOrganization } from '../types';\nimport { computeTotalCountFromGitHubToken, handleError } from './utils';\n\nexport async function getAllAppOrgs(\n githubCredentialsProvider: CustomGithubCredentialsProvider,\n ghConfig: GithubIntegrationConfig,\n credentialAccountLogin?: string,\n) {\n const result = new Map<string, GithubOrganization>();\n const resp = await githubCredentialsProvider.getAllAppInstallations(ghConfig);\n for (const installation of resp ?? []) {\n if (\n !(\n installation.account &&\n installation.target_type?.toLowerCase() === 'organization'\n )\n ) {\n continue;\n }\n const acc = installation.account!;\n if (credentialAccountLogin !== acc.login) {\n continue;\n }\n result.set(acc.url, {\n id: acc.id,\n description: acc.description ?? undefined,\n name: acc.login,\n url: acc.html_url,\n html_url: acc.html_url,\n repos_url: acc.repos_url,\n events_url: acc.events_url,\n });\n }\n return result;\n}\n\nexport async function addGithubAppOrgs(\n deps: {\n logger: LoggerService;\n githubCredentialsProvider: CustomGithubCredentialsProvider;\n },\n octokit: Octokit,\n ghConfig: GithubIntegrationConfig,\n params: {\n credentialAccountLogin: string | undefined;\n search: string | undefined;\n orgs: Map<string, GithubOrganization>;\n errors: Map<number, GithubFetchError>;\n },\n): Promise<{ totalCount?: number }> {\n const credentialAccountLogin = params.credentialAccountLogin;\n const search = params.search;\n const orgs = params.orgs;\n const errors = params.errors;\n let totalCount = 0;\n try {\n const resp = await getAllAppOrgs(\n deps.githubCredentialsProvider,\n ghConfig,\n credentialAccountLogin,\n );\n for (const [orgUrl, ghOrg] of resp) {\n if (search && !ghOrg.name.toLowerCase().includes(search.toLowerCase())) {\n continue;\n }\n const orgData = await octokit.request(\n orgUrl.replace('/users/', '/orgs/'),\n );\n orgs.set(ghOrg.name, {\n ...ghOrg,\n public_repos: orgData?.data?.public_repos,\n total_private_repos: orgData?.data?.total_private_repos,\n owned_private_repos: orgData?.data?.owned_private_repos,\n });\n totalCount++;\n }\n } catch (err: any) {\n logErrorIfNeeded(\n deps.logger,\n `Fetching organizations with access token for github app`,\n err,\n );\n errors.set(-1, err.message);\n }\n return { totalCount };\n}\n\nexport async function addGithubTokenOrgs(\n deps: {\n logger: LoggerService;\n },\n octokit: Octokit,\n credential: GithubCredentials,\n params: {\n search: string | undefined;\n orgs: Map<string, GithubOrganization>;\n errors: Map<number, GithubFetchError>;\n pageNumber: number;\n pageSize: number;\n },\n): Promise<{ totalCount?: number }> {\n const search = params.search;\n const orgs = params.orgs;\n const errors = params.errors;\n const pageNumber = params.pageNumber ?? DefaultPageNumber;\n const pageSize = params.pageSize ?? DefaultPageSize;\n\n let totalCount: number | undefined;\n try {\n let matchingOrgs;\n if (search) {\n // Initial idea was to leverage octokit.rest.search.users({q: `${search} type:org`}),\n // but this searches across all of GitHub, not only the orgs accessible by the current creds.\n // That's why we are searching in everything, hoping the list of organizations to not be that big in size\n const resp = await octokit.paginate(\n octokit.rest.orgs.listForAuthenticatedUser,\n {\n sort: 'full_name',\n direction: 'asc',\n },\n );\n const allMatchingOrgs =\n resp?.filter(org =>\n org.login.toLowerCase().includes(search.toLowerCase()),\n ) ?? [];\n const matchingOrgsPage = paginateArray(\n allMatchingOrgs,\n pageNumber,\n pageSize,\n );\n matchingOrgs = matchingOrgsPage.result;\n totalCount = matchingOrgsPage.totalCount;\n } else {\n /**\n * The listForAuthenticatedUser endpoint will grab all the repositories the github token has explicit access to.\n * These would include repositories they own, repositories where they are a collaborator,\n * and repositories that they can access through an organization membership.\n */\n const resp = await octokit.rest.orgs.listForAuthenticatedUser({\n page: pageNumber,\n per_page: pageSize,\n sort: 'full_name',\n direction: 'asc',\n });\n matchingOrgs = resp?.data ?? [];\n\n totalCount = await computeTotalCountFromGitHubToken(\n deps,\n async (lastPageNumber: number) =>\n octokit.orgs\n .listForAuthenticatedUser({\n page: lastPageNumber,\n per_page: 100,\n })\n .then(lastPageResp => lastPageResp.data.length),\n 'orgs.listForAuthenticatedUser',\n resp?.data?.length,\n resp?.headers?.link,\n );\n }\n\n for (const org of matchingOrgs) {\n const orgData = await octokit.request(org.url);\n const ghOrg: GithubOrganization = {\n id: org.id,\n name: org.login,\n description: org.description ?? undefined,\n url: orgData?.data?.html_url ?? org.url,\n repos_url: org.repos_url,\n hooks_url: org.hooks_url,\n issues_url: org.issues_url,\n members_url: org.members_url,\n public_members_url: org.public_members_url,\n avatar_url: org.avatar_url,\n public_repos: orgData?.data?.public_repos,\n total_private_repos: orgData?.data?.total_private_repos,\n owned_private_repos: orgData?.data?.owned_private_repos,\n };\n orgs.set(org.login, ghOrg);\n }\n } catch (err) {\n handleError(\n { logger: deps.logger },\n 'Fetching orgs with token from token',\n credential,\n errors,\n err,\n );\n }\n return { totalCount };\n}\n"],"names":["logErrorIfNeeded","DefaultPageNumber","DefaultPageSize","paginateArray","computeTotalCountFromGitHubToken","handleError"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ var catalogUtils = require('../../catalog/catalogUtils.cjs.js');
4
+ require('@backstage/errors');
5
+ require('@backstage/plugin-permission-common');
6
+ require('@red-hat-developer-hub/backstage-plugin-bulk-import-common');
7
+ var loggingUtils = require('../../helpers/loggingUtils.cjs.js');
8
+
9
+ async function findOpenPRForBranch(logger, config, octo, owner, repo, branchName, withCatalogInfoContent = false) {
10
+ try {
11
+ const response = await octo.rest.pulls.list({
12
+ owner,
13
+ repo,
14
+ state: "open"
15
+ });
16
+ for (const pull of response.data) {
17
+ if (pull.head.ref === branchName) {
18
+ return {
19
+ prNum: pull.number,
20
+ prUrl: pull.html_url,
21
+ prTitle: pull.title,
22
+ prBody: pull.body ?? void 0,
23
+ prCatalogInfoContent: withCatalogInfoContent ? await getCatalogInfoContentFromPR(
24
+ logger,
25
+ config,
26
+ octo,
27
+ owner,
28
+ repo,
29
+ pull.number,
30
+ pull.head.sha
31
+ ) : void 0,
32
+ lastUpdate: pull.updated_at
33
+ };
34
+ }
35
+ }
36
+ } catch (error) {
37
+ loggingUtils.logErrorIfNeeded(logger, "Error fetching pull requests", error);
38
+ }
39
+ return {};
40
+ }
41
+ async function getCatalogInfoContentFromPR(logger, config, octo, owner, repo, prNumber, prHeadSha) {
42
+ try {
43
+ const filePath = catalogUtils.getCatalogFilename(config);
44
+ const fileContentResponse = await octo.rest.repos.getContent({
45
+ owner,
46
+ repo,
47
+ path: filePath,
48
+ ref: prHeadSha
49
+ });
50
+ if (!fileContentResponse.data) {
51
+ return void 0;
52
+ }
53
+ if (!("content" in fileContentResponse.data)) {
54
+ return void 0;
55
+ }
56
+ return Buffer.from(fileContentResponse.data.content, "base64").toString(
57
+ "utf-8"
58
+ );
59
+ } catch (error) {
60
+ loggingUtils.logErrorIfNeeded(
61
+ logger,
62
+ `Error fetching catalog-info content from PR ${prNumber}`,
63
+ error
64
+ );
65
+ return void 0;
66
+ }
67
+ }
68
+ async function closePRWithComment(octo, owner, repo, prNum, comment) {
69
+ await octo.rest.issues.createComment({
70
+ owner,
71
+ repo,
72
+ issue_number: prNum,
73
+ body: comment
74
+ });
75
+ await octo.rest.pulls.update({
76
+ owner,
77
+ repo,
78
+ pull_number: prNum,
79
+ state: "closed"
80
+ });
81
+ }
82
+
83
+ exports.closePRWithComment = closePRWithComment;
84
+ exports.findOpenPRForBranch = findOpenPRForBranch;
85
+ //# sourceMappingURL=prUtils.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prUtils.cjs.js","sources":["../../../src/github/utils/prUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\n\nimport { Octokit } from '@octokit/rest';\n\nimport { getCatalogFilename } from '../../catalog/catalogUtils';\nimport { logErrorIfNeeded } from '../../helpers';\n\nexport async function findOpenPRForBranch(\n logger: LoggerService,\n config: Config,\n octo: Octokit,\n owner: string,\n repo: string,\n branchName: string,\n withCatalogInfoContent: boolean = false,\n): Promise<{\n prNum?: number;\n prUrl?: string;\n prTitle?: string;\n prBody?: string;\n prCatalogInfoContent?: string;\n lastUpdate?: string;\n}> {\n try {\n const response = await octo.rest.pulls.list({\n owner: owner,\n repo: repo,\n state: 'open',\n });\n for (const pull of response.data) {\n if (pull.head.ref === branchName) {\n return {\n prNum: pull.number,\n prUrl: pull.html_url,\n prTitle: pull.title,\n prBody: pull.body ?? undefined,\n prCatalogInfoContent: withCatalogInfoContent\n ? await getCatalogInfoContentFromPR(\n logger,\n config,\n octo,\n owner,\n repo,\n pull.number,\n pull.head.sha,\n )\n : undefined,\n lastUpdate: pull.updated_at,\n };\n }\n }\n } catch (error) {\n logErrorIfNeeded(logger, 'Error fetching pull requests', error);\n }\n return {};\n}\n\nasync function getCatalogInfoContentFromPR(\n logger: LoggerService,\n config: Config,\n octo: Octokit,\n owner: string,\n repo: string,\n prNumber: number,\n prHeadSha: string,\n): Promise<string | undefined> {\n try {\n const filePath = getCatalogFilename(config);\n const fileContentResponse = await octo.rest.repos.getContent({\n owner,\n repo,\n path: filePath,\n ref: prHeadSha,\n });\n if (!fileContentResponse.data) {\n return undefined;\n }\n if (!('content' in fileContentResponse.data)) {\n return undefined;\n }\n return Buffer.from(fileContentResponse.data.content, 'base64').toString(\n 'utf-8',\n );\n } catch (error: any) {\n logErrorIfNeeded(\n logger,\n `Error fetching catalog-info content from PR ${prNumber}`,\n error,\n );\n return undefined;\n }\n}\n\nexport async function closePRWithComment(\n octo: Octokit,\n owner: string,\n repo: string,\n prNum: number,\n comment: string,\n) {\n await octo.rest.issues.createComment({\n owner,\n repo,\n issue_number: prNum,\n body: comment,\n });\n await octo.rest.pulls.update({\n owner,\n repo,\n pull_number: prNum,\n state: 'closed',\n });\n}\n"],"names":["logErrorIfNeeded","getCatalogFilename"],"mappings":"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"}