diff --git a/dist/431.index.js b/dist/431.index.js index e8d81dd8..a4372912 100644 --- a/dist/431.index.js +++ b/dist/431.index.js @@ -115,7 +115,7 @@ const approvalsSatisfied = async ({ teams, users, number_of_reviewers = '1', req }); const codeOwnerLogins = (0,lodash.uniq)(loginsLists.flat()); const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length; - const numberOfRequiredReviews = teamOverrides?.find(({ team }) => entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; + const numberOfRequiredReviews = teamOverrides?.find(({ team }) => team && entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`); core.info(`Number of required reviews: ${numberOfRequiredReviews}`); return numberOfApprovals >= Number(numberOfRequiredReviews); diff --git a/dist/431.index.js.map b/dist/431.index.js.map index dcdbe7c8..38d99db8 100644 --- a/dist/431.index.js.map +++ b/dist/431.index.js.map @@ -1 +1 @@ -{"version":3,"file":"431.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AC5BA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAMA;AAEA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;AC1HA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AAiDA;;;;;;;;;;;AC9DA;;;;;;;;;;;AAWA;AAEA;;;;;;;;;;;;;;ACbA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA","sources":[".././src/utils/paginate-all-reviews.ts",".././src/helpers/approvals-satisfied.ts",".././src/octokit.ts",".././src/types/generated.ts",".././src/utils/convert-to-team-slug.ts",".././src/utils/get-changed-filepaths.ts",".././src/utils/get-core-member-logins.ts"],"sourcesContent":["/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestReviewList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllReviews = async (prNumber: number, page = 1): Promise => {\n const response = await octokit.pulls.listReviews({\n pull_number: prNumber,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllReviews(prNumber, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { getRequiredCodeOwnersEntries } from '../utils/get-core-member-logins';\nimport { map } from 'bluebird';\nimport { convertToTeamSlug } from '../utils/convert-to-team-slug';\nimport { CodeOwnersEntry } from 'codeowners-utils';\nimport * as core from '@actions/core';\nimport { paginateAllReviews } from '../utils/paginate-all-reviews';\nimport { uniq, uniqBy } from 'lodash';\n\nexport class ApprovalsSatisfied extends HelperInputs {\n teams?: string;\n users?: string;\n number_of_reviewers?: string;\n required_review_overrides?: string;\n pull_number?: string;\n}\n\nexport const approvalsSatisfied = async ({\n teams,\n users,\n number_of_reviewers = '1',\n required_review_overrides,\n pull_number\n}: ApprovalsSatisfied = {}) => {\n const prNumber = pull_number ? Number(pull_number) : context.issue.number;\n\n const teamOverrides = required_review_overrides?.split(',').map(overrideString => {\n const [team, numberOfRequiredReviews] = overrideString.split(':');\n return { team, numberOfRequiredReviews };\n });\n const teamsList = updateTeamsList(teams?.split('\\n'));\n if (!validateTeamsList(teamsList)) {\n core.setFailed('If teams input is in the format \"org/team\", then the org must be the same as the repository org');\n return false;\n }\n const usersList = users?.split('\\n');\n\n const reviews = await paginateAllReviews(prNumber);\n const approverLogins = reviews\n .filter(({ state }) => state === 'APPROVED')\n .map(({ user }) => user?.login)\n .filter(Boolean);\n core.info(`PR already approved by: ${approverLogins.toString()}`);\n\n const requiredCodeOwnersEntries =\n teamsList || usersList\n ? createArtificialCodeOwnersEntry({ teams: teamsList, users: usersList })\n : await getRequiredCodeOwnersEntries(prNumber);\n const requiredCodeOwnersEntriesWithOwners = uniqBy(\n requiredCodeOwnersEntries.filter(({ owners }) => owners.length),\n 'owners'\n );\n\n const codeOwnersEntrySatisfiesApprovals = async (entry: Pick) => {\n const loginsLists = await map(entry.owners, async teamOrUsers => {\n if (isTeam(teamOrUsers)) {\n return await fetchTeamLogins(teamOrUsers);\n } else {\n return teamOrUsers.replaceAll('@', '').split(',');\n }\n });\n const codeOwnerLogins = uniq(loginsLists.flat());\n\n const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length;\n\n const numberOfRequiredReviews =\n teamOverrides?.find(({ team }) => entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers;\n core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`);\n core.info(`Number of required reviews: ${numberOfRequiredReviews}`);\n\n return numberOfApprovals >= Number(numberOfRequiredReviews);\n };\n\n core.info(`Required code owners: ${requiredCodeOwnersEntriesWithOwners.map(({ owners }) => owners).toString()}`);\n const booleans = await Promise.all(requiredCodeOwnersEntriesWithOwners.map(codeOwnersEntrySatisfiesApprovals));\n return booleans.every(Boolean);\n};\n\nconst createArtificialCodeOwnersEntry = ({ teams = [], users = [] }: { teams?: string[]; users?: string[] }) => [\n { owners: teams.concat(users) }\n];\nconst isTeam = (teamOrUsers: string) => teamOrUsers.includes('/');\nconst fetchTeamLogins = async (team: string) => {\n const { data } = await octokit.teams.listMembersInOrg({\n org: context.repo.owner,\n team_slug: convertToTeamSlug(team),\n per_page: 100\n });\n return data.map(({ login }) => login);\n};\nconst updateTeamsList = (teamsList?: string[]) => {\n return teamsList?.map(team => {\n if (!team.includes('/')) {\n return `${context.repo.owner}/${team}`;\n } else {\n return team;\n }\n });\n};\n\nconst validateTeamsList = (teamsList?: string[]) => {\n return (\n teamsList?.every(team => {\n const inputOrg = team.split('/')[0];\n return inputOrg === context.repo.owner;\n }) ?? true\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const convertToTeamSlug = (codeOwner: string) => codeOwner.substring(codeOwner.indexOf('/') + 1);\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { ChangedFilesList } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport const getChangedFilepaths = async (pull_number: number, ignore_deleted?: boolean) => {\n const changedFiles = await paginateAllChangedFilepaths(pull_number);\n const filesToMap = ignore_deleted ? changedFiles.filter(file => file.status !== 'removed') : changedFiles;\n return filesToMap.map(file => file.filename);\n};\n\nconst paginateAllChangedFilepaths = async (pull_number: number, page = 1): Promise => {\n const response = await octokit.pulls.listFiles({\n pull_number,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllChangedFilepaths(pull_number, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { CodeOwnersEntry, loadOwners, matchFile } from 'codeowners-utils';\nimport { uniq, union } from 'lodash';\nimport { context } from '@actions/github';\nimport { getChangedFilepaths } from './get-changed-filepaths';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { convertToTeamSlug } from './convert-to-team-slug';\n\nexport const getCoreMemberLogins = async (pull_number: number, teams?: string[]) => {\n const codeOwners = teams ?? getCodeOwnersFromEntries(await getRequiredCodeOwnersEntries(pull_number));\n const teamsAndLogins = await getCoreTeamsAndLogins(codeOwners);\n return uniq(teamsAndLogins.map(({ login }) => login));\n};\n\nexport const getRequiredCodeOwnersEntries = async (pull_number: number): Promise => {\n const codeOwners = (await loadOwners(process.cwd())) ?? [];\n const changedFilePaths = await getChangedFilepaths(pull_number);\n return changedFilePaths.map(filePath => matchFile(filePath, codeOwners)).filter(Boolean);\n};\n\nconst getCoreTeamsAndLogins = async (codeOwners?: string[]) => {\n if (!codeOwners?.length) {\n core.setFailed('No code owners found. Please provide a \"teams\" input or set up a CODEOWNERS file in your repo.');\n throw new Error();\n }\n\n const teamsAndLogins = await map(codeOwners, async team =>\n octokit.teams\n .listMembersInOrg({\n org: context.repo.owner,\n team_slug: team,\n per_page: 100\n })\n .then(listMembersResponse => listMembersResponse.data.map(({ login }) => ({ team, login })))\n );\n return union(...teamsAndLogins);\n};\n\nconst getCodeOwnersFromEntries = (codeOwnersEntries: CodeOwnersEntry[]) => {\n return uniq(\n codeOwnersEntries\n .map(entry => entry.owners)\n .flat()\n .filter(Boolean)\n .map(codeOwner => convertToTeamSlug(codeOwner))\n );\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"431.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AC5BA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAMA;AAEA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;AC1HA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AAiDA;;;;;;;;;;;AC9DA;;;;;;;;;;;AAWA;AAEA;;;;;;;;;;;;;;ACbA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA","sources":[".././src/utils/paginate-all-reviews.ts",".././src/helpers/approvals-satisfied.ts",".././src/octokit.ts",".././src/types/generated.ts",".././src/utils/convert-to-team-slug.ts",".././src/utils/get-changed-filepaths.ts",".././src/utils/get-core-member-logins.ts"],"sourcesContent":["/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestReviewList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllReviews = async (prNumber: number, page = 1): Promise => {\n const response = await octokit.pulls.listReviews({\n pull_number: prNumber,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllReviews(prNumber, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { getRequiredCodeOwnersEntries } from '../utils/get-core-member-logins';\nimport { map } from 'bluebird';\nimport { convertToTeamSlug } from '../utils/convert-to-team-slug';\nimport { CodeOwnersEntry } from 'codeowners-utils';\nimport * as core from '@actions/core';\nimport { paginateAllReviews } from '../utils/paginate-all-reviews';\nimport { uniq, uniqBy } from 'lodash';\n\nexport class ApprovalsSatisfied extends HelperInputs {\n teams?: string;\n users?: string;\n number_of_reviewers?: string;\n required_review_overrides?: string;\n pull_number?: string;\n}\n\nexport const approvalsSatisfied = async ({\n teams,\n users,\n number_of_reviewers = '1',\n required_review_overrides,\n pull_number\n}: ApprovalsSatisfied = {}) => {\n const prNumber = pull_number ? Number(pull_number) : context.issue.number;\n\n const teamOverrides = required_review_overrides?.split(',').map(overrideString => {\n const [team, numberOfRequiredReviews] = overrideString.split(':');\n return { team, numberOfRequiredReviews };\n });\n const teamsList = updateTeamsList(teams?.split('\\n'));\n if (!validateTeamsList(teamsList)) {\n core.setFailed('If teams input is in the format \"org/team\", then the org must be the same as the repository org');\n return false;\n }\n const usersList = users?.split('\\n');\n\n const reviews = await paginateAllReviews(prNumber);\n const approverLogins = reviews\n .filter(({ state }) => state === 'APPROVED')\n .map(({ user }) => user?.login)\n .filter(Boolean);\n core.info(`PR already approved by: ${approverLogins.toString()}`);\n\n const requiredCodeOwnersEntries =\n teamsList || usersList\n ? createArtificialCodeOwnersEntry({ teams: teamsList, users: usersList })\n : await getRequiredCodeOwnersEntries(prNumber);\n const requiredCodeOwnersEntriesWithOwners = uniqBy(\n requiredCodeOwnersEntries.filter(({ owners }) => owners.length),\n 'owners'\n );\n\n const codeOwnersEntrySatisfiesApprovals = async (entry: Pick) => {\n const loginsLists = await map(entry.owners, async teamOrUsers => {\n if (isTeam(teamOrUsers)) {\n return await fetchTeamLogins(teamOrUsers);\n } else {\n return teamOrUsers.replaceAll('@', '').split(',');\n }\n });\n const codeOwnerLogins = uniq(loginsLists.flat());\n\n const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length;\n\n const numberOfRequiredReviews =\n teamOverrides?.find(({ team }) => team && entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers;\n core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`);\n core.info(`Number of required reviews: ${numberOfRequiredReviews}`);\n\n return numberOfApprovals >= Number(numberOfRequiredReviews);\n };\n\n core.info(`Required code owners: ${requiredCodeOwnersEntriesWithOwners.map(({ owners }) => owners).toString()}`);\n const booleans = await Promise.all(requiredCodeOwnersEntriesWithOwners.map(codeOwnersEntrySatisfiesApprovals));\n return booleans.every(Boolean);\n};\n\nconst createArtificialCodeOwnersEntry = ({ teams = [], users = [] }: { teams?: string[]; users?: string[] }) => [\n { owners: teams.concat(users) }\n];\nconst isTeam = (teamOrUsers: string) => teamOrUsers.includes('/');\nconst fetchTeamLogins = async (team: string) => {\n const { data } = await octokit.teams.listMembersInOrg({\n org: context.repo.owner,\n team_slug: convertToTeamSlug(team),\n per_page: 100\n });\n return data.map(({ login }) => login);\n};\nconst updateTeamsList = (teamsList?: string[]) => {\n return teamsList?.map(team => {\n if (!team.includes('/')) {\n return `${context.repo.owner}/${team}`;\n } else {\n return team;\n }\n });\n};\n\nconst validateTeamsList = (teamsList?: string[]) => {\n return (\n teamsList?.every(team => {\n const inputOrg = team.split('/')[0];\n return inputOrg === context.repo.owner;\n }) ?? true\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const convertToTeamSlug = (codeOwner: string) => codeOwner.substring(codeOwner.indexOf('/') + 1);\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { ChangedFilesList } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport const getChangedFilepaths = async (pull_number: number, ignore_deleted?: boolean) => {\n const changedFiles = await paginateAllChangedFilepaths(pull_number);\n const filesToMap = ignore_deleted ? changedFiles.filter(file => file.status !== 'removed') : changedFiles;\n return filesToMap.map(file => file.filename);\n};\n\nconst paginateAllChangedFilepaths = async (pull_number: number, page = 1): Promise => {\n const response = await octokit.pulls.listFiles({\n pull_number,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllChangedFilepaths(pull_number, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { CodeOwnersEntry, loadOwners, matchFile } from 'codeowners-utils';\nimport { uniq, union } from 'lodash';\nimport { context } from '@actions/github';\nimport { getChangedFilepaths } from './get-changed-filepaths';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { convertToTeamSlug } from './convert-to-team-slug';\n\nexport const getCoreMemberLogins = async (pull_number: number, teams?: string[]) => {\n const codeOwners = teams ?? getCodeOwnersFromEntries(await getRequiredCodeOwnersEntries(pull_number));\n const teamsAndLogins = await getCoreTeamsAndLogins(codeOwners);\n return uniq(teamsAndLogins.map(({ login }) => login));\n};\n\nexport const getRequiredCodeOwnersEntries = async (pull_number: number): Promise => {\n const codeOwners = (await loadOwners(process.cwd())) ?? [];\n const changedFilePaths = await getChangedFilepaths(pull_number);\n return changedFilePaths.map(filePath => matchFile(filePath, codeOwners)).filter(Boolean);\n};\n\nconst getCoreTeamsAndLogins = async (codeOwners?: string[]) => {\n if (!codeOwners?.length) {\n core.setFailed('No code owners found. Please provide a \"teams\" input or set up a CODEOWNERS file in your repo.');\n throw new Error();\n }\n\n const teamsAndLogins = await map(codeOwners, async team =>\n octokit.teams\n .listMembersInOrg({\n org: context.repo.owner,\n team_slug: team,\n per_page: 100\n })\n .then(listMembersResponse => listMembersResponse.data.map(({ login }) => ({ team, login })))\n );\n return union(...teamsAndLogins);\n};\n\nconst getCodeOwnersFromEntries = (codeOwnersEntries: CodeOwnersEntry[]) => {\n return uniq(\n codeOwnersEntries\n .map(entry => entry.owners)\n .flat()\n .filter(Boolean)\n .map(codeOwner => convertToTeamSlug(codeOwner))\n );\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/dist/676.index.js b/dist/676.index.js index 8361c788..f69c07f5 100644 --- a/dist/676.index.js +++ b/dist/676.index.js @@ -201,7 +201,7 @@ const approvalsSatisfied = async ({ teams, users, number_of_reviewers = '1', req }); const codeOwnerLogins = (0,lodash.uniq)(loginsLists.flat()); const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length; - const numberOfRequiredReviews = teamOverrides?.find(({ team }) => entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; + const numberOfRequiredReviews = teamOverrides?.find(({ team }) => team && entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`); core.info(`Number of required reviews: ${numberOfRequiredReviews}`); return numberOfApprovals >= Number(numberOfRequiredReviews); diff --git a/dist/676.index.js.map b/dist/676.index.js.map index b223a72b..cd39d74a 100644 --- a/dist/676.index.js.map +++ b/dist/676.index.js.map @@ -1 +1 @@ -{"version":3,"file":"676.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3DA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AC5BA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAMA;AAEA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;AC1HA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AAMA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;ACvFA;;;;;;;;;;;AAWA;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;;;AAIA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;ACjIA;;;;;;;;;;;AAWA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAAA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AClEA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;ACrCA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;ACrDA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AAiDA;;;;;;;;;;;AC9DA;;;;;;;;;;;AAWA;AAEA;;;;;;;;;;;;;;ACbA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;AC5DA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AAQA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChDA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":[".././src/constants.ts",".././src/utils/paginate-all-reviews.ts",".././src/helpers/approvals-satisfied.ts",".././src/helpers/create-pr-comment.ts",".././src/utils/update-merge-queue.ts",".././src/helpers/manage-merge-queue.ts",".././src/helpers/prepare-queued-pr-for-merge.ts",".././src/helpers/remove-label.ts",".././src/helpers/set-commit-status.ts",".././src/octokit.ts",".././src/types/generated.ts",".././src/utils/convert-to-team-slug.ts",".././src/utils/get-changed-filepaths.ts",".././src/utils/get-core-member-logins.ts",".././src/utils/notify-user.ts",".././src/utils/paginate-open-pull-requests.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// These extra headers are for experimental API features on Github Enterprise. See https://docs.github.com/en/enterprise-server@3.0/rest/overview/api-previews for details.\nconst PREVIEWS = ['ant-man', 'flash', 'groot', 'inertia', 'starfox'];\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: PREVIEWS.map(preview => `application/vnd.github.${preview}-preview+json`).join()\n }\n};\n\nexport const SECONDS_IN_A_DAY = 86400000;\nexport const DEFAULT_EXEMPT_DESCRIPTION = 'Passed in case the check is exempt.';\nexport const DEFAULT_PIPELINE_STATUS = 'Pipeline Status';\nexport const DEFAULT_PIPELINE_DESCRIPTION = 'Pipeline clear.';\nexport const PRODUCTION_ENVIRONMENT = 'production';\nexport const LATE_REVIEW = 'Late Review';\nexport const OVERDUE_ISSUE = 'Overdue';\nexport const ALMOST_OVERDUE_ISSUE = 'Due Soon';\nexport const PRIORITY_1 = 'Priority: Critical';\nexport const PRIORITY_2 = 'Priority: High';\nexport const PRIORITY_3 = 'Priority: Medium';\nexport const PRIORITY_4 = 'Priority: Low';\nexport const PRIORITY_LABELS = [PRIORITY_1, PRIORITY_2, PRIORITY_3, PRIORITY_4] as const;\nexport const PRIORITY_TO_DAYS_MAP = {\n [PRIORITY_1]: 2,\n [PRIORITY_2]: 14,\n [PRIORITY_3]: 45,\n [PRIORITY_4]: 90\n};\nexport const CORE_APPROVED_PR_LABEL = 'CORE APPROVED';\nexport const PEER_APPROVED_PR_LABEL = 'PEER APPROVED';\nexport const READY_FOR_MERGE_PR_LABEL = 'READY FOR MERGE';\nexport const MERGE_QUEUE_STATUS = 'QUEUE CHECKER';\nexport const QUEUED_FOR_MERGE_PREFIX = 'QUEUED FOR MERGE';\nexport const FIRST_QUEUED_PR_LABEL = `${QUEUED_FOR_MERGE_PREFIX} #1`;\nexport const JUMP_THE_QUEUE_PR_LABEL = 'JUMP THE QUEUE';\nexport const DEFAULT_PR_TITLE_REGEX = '^(build|ci|chore|docs|feat|fix|perf|refactor|style|test|revert|Revert|BREAKING CHANGE)((.*))?: .+$';\nexport const COPYRIGHT_HEADER = `/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/`;\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestReviewList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllReviews = async (prNumber: number, page = 1): Promise => {\n const response = await octokit.pulls.listReviews({\n pull_number: prNumber,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllReviews(prNumber, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { getRequiredCodeOwnersEntries } from '../utils/get-core-member-logins';\nimport { map } from 'bluebird';\nimport { convertToTeamSlug } from '../utils/convert-to-team-slug';\nimport { CodeOwnersEntry } from 'codeowners-utils';\nimport * as core from '@actions/core';\nimport { paginateAllReviews } from '../utils/paginate-all-reviews';\nimport { uniq, uniqBy } from 'lodash';\n\nexport class ApprovalsSatisfied extends HelperInputs {\n teams?: string;\n users?: string;\n number_of_reviewers?: string;\n required_review_overrides?: string;\n pull_number?: string;\n}\n\nexport const approvalsSatisfied = async ({\n teams,\n users,\n number_of_reviewers = '1',\n required_review_overrides,\n pull_number\n}: ApprovalsSatisfied = {}) => {\n const prNumber = pull_number ? Number(pull_number) : context.issue.number;\n\n const teamOverrides = required_review_overrides?.split(',').map(overrideString => {\n const [team, numberOfRequiredReviews] = overrideString.split(':');\n return { team, numberOfRequiredReviews };\n });\n const teamsList = updateTeamsList(teams?.split('\\n'));\n if (!validateTeamsList(teamsList)) {\n core.setFailed('If teams input is in the format \"org/team\", then the org must be the same as the repository org');\n return false;\n }\n const usersList = users?.split('\\n');\n\n const reviews = await paginateAllReviews(prNumber);\n const approverLogins = reviews\n .filter(({ state }) => state === 'APPROVED')\n .map(({ user }) => user?.login)\n .filter(Boolean);\n core.info(`PR already approved by: ${approverLogins.toString()}`);\n\n const requiredCodeOwnersEntries =\n teamsList || usersList\n ? createArtificialCodeOwnersEntry({ teams: teamsList, users: usersList })\n : await getRequiredCodeOwnersEntries(prNumber);\n const requiredCodeOwnersEntriesWithOwners = uniqBy(\n requiredCodeOwnersEntries.filter(({ owners }) => owners.length),\n 'owners'\n );\n\n const codeOwnersEntrySatisfiesApprovals = async (entry: Pick) => {\n const loginsLists = await map(entry.owners, async teamOrUsers => {\n if (isTeam(teamOrUsers)) {\n return await fetchTeamLogins(teamOrUsers);\n } else {\n return teamOrUsers.replaceAll('@', '').split(',');\n }\n });\n const codeOwnerLogins = uniq(loginsLists.flat());\n\n const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length;\n\n const numberOfRequiredReviews =\n teamOverrides?.find(({ team }) => entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers;\n core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`);\n core.info(`Number of required reviews: ${numberOfRequiredReviews}`);\n\n return numberOfApprovals >= Number(numberOfRequiredReviews);\n };\n\n core.info(`Required code owners: ${requiredCodeOwnersEntriesWithOwners.map(({ owners }) => owners).toString()}`);\n const booleans = await Promise.all(requiredCodeOwnersEntriesWithOwners.map(codeOwnersEntrySatisfiesApprovals));\n return booleans.every(Boolean);\n};\n\nconst createArtificialCodeOwnersEntry = ({ teams = [], users = [] }: { teams?: string[]; users?: string[] }) => [\n { owners: teams.concat(users) }\n];\nconst isTeam = (teamOrUsers: string) => teamOrUsers.includes('/');\nconst fetchTeamLogins = async (team: string) => {\n const { data } = await octokit.teams.listMembersInOrg({\n org: context.repo.owner,\n team_slug: convertToTeamSlug(team),\n per_page: 100\n });\n return data.map(({ login }) => login);\n};\nconst updateTeamsList = (teamsList?: string[]) => {\n return teamsList?.map(team => {\n if (!team.includes('/')) {\n return `${context.repo.owner}/${team}`;\n } else {\n return team;\n }\n });\n};\n\nconst validateTeamsList = (teamsList?: string[]) => {\n return (\n teamsList?.every(team => {\n const inputOrg = team.split('/')[0];\n return inputOrg === context.repo.owner;\n }) ?? true\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { GITHUB_OPTIONS } from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class CreatePrComment extends HelperInputs {\n body = '';\n sha?: string;\n login?: string;\n pull_number?: string;\n repo_name?: string;\n repo_owner_name?: string;\n}\n\nconst emptyResponse = { data: [] };\n\nconst getFirstPrByCommit = async (sha?: string, repo_name?: string, repo_owner_name?: string) => {\n const prs =\n (sha &&\n (await octokit.repos.listPullRequestsAssociatedWithCommit({\n commit_sha: sha,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner,\n ...GITHUB_OPTIONS\n }))) ||\n emptyResponse;\n\n return prs.data.find(Boolean)?.number;\n};\n\nconst getCommentByUser = async (login?: string, pull_number?: string, repo_name?: string, repo_owner_name?: string) => {\n const comments =\n (login &&\n (await octokit.issues.listComments({\n issue_number: pull_number ? Number(pull_number) : context.issue.number,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n }))) ||\n emptyResponse;\n\n return comments.data.find(comment => comment?.user?.login === login)?.id;\n};\n\nexport const createPrComment = async ({ body, sha, login, pull_number, repo_name, repo_owner_name }: CreatePrComment) => {\n const defaultPrNumber = context.issue.number;\n\n if (!sha && !login) {\n return octokit.issues.createComment({\n body,\n issue_number: pull_number ? Number(pull_number) : defaultPrNumber,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n }\n\n const prNumber = (await getFirstPrByCommit(sha, repo_name, repo_owner_name)) ?? (pull_number ? Number(pull_number) : defaultPrNumber);\n const commentId = await getCommentByUser(login, pull_number, repo_name, repo_owner_name);\n\n if (commentId) {\n return octokit.issues.updateComment({\n comment_id: commentId,\n body,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n } else {\n return octokit.issues.createComment({\n body,\n issue_number: prNumber,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants';\nimport { PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { updatePrWithDefaultBranch } from '../helpers/prepare-queued-pr-for-merge';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestList) => {\n const sortedPrs = sortPrsByQueuePosition(queuedPrs);\n return map(sortedPrs, updateQueuePosition);\n};\n\nconst sortPrsByQueuePosition = (queuedPrs: PullRequestList) =>\n queuedPrs\n .map(pr => {\n const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL));\n const queuePosition = isJumpingTheQueue ? 0 : Number(label?.split('#')?.[1]);\n return {\n number: pr.number,\n label,\n queuePosition,\n sha: pr.head.sha\n };\n })\n .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition);\n\nconst updateQueuePosition = async (pr: ReturnType[number], index: number) => {\n const { number, label, queuePosition, sha } = pr;\n const newQueuePosition = index + 1;\n if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) {\n return;\n }\n const prIsNowFirstInQueue = newQueuePosition === 1;\n if (prIsNowFirstInQueue) {\n const { data: firstPrInQueue } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithDefaultBranch(firstPrInQueue)]);\n const {\n data: {\n head: { sha: updatedHeadSha }\n }\n } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number),\n setCommitStatus({\n sha: updatedHeadSha,\n context: MERGE_QUEUE_STATUS,\n state: 'success',\n description: 'This PR is next to merge.'\n })\n ]);\n }\n\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number),\n setCommitStatus({\n sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is in line to merge.'\n })\n ]);\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport {\n FIRST_QUEUED_PR_LABEL,\n JUMP_THE_QUEUE_PR_LABEL,\n MERGE_QUEUE_STATUS,\n QUEUED_FOR_MERGE_PREFIX,\n READY_FOR_MERGE_PR_LABEL\n} from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { notifyUser } from '../utils/notify-user';\nimport { octokit, octokitGraphql } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\nimport { updatePrWithDefaultBranch } from './prepare-queued-pr-for-merge';\nimport { approvalsSatisfied } from './approvals-satisfied';\nimport { createPrComment } from './create-pr-comment';\n\nexport class ManageMergeQueue extends HelperInputs {\n login?: string;\n slack_webhook_url?: string;\n skip_auto_merge?: string;\n}\n\nexport const manageMergeQueue = async ({ login, slack_webhook_url, skip_auto_merge }: ManageMergeQueue = {}) => {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: context.issue.number, ...context.repo });\n if (pullRequest.merged || !pullRequest.labels.find(label => label.name === READY_FOR_MERGE_PR_LABEL)) {\n core.info('This PR is not in the merge queue.');\n return removePrFromQueue(pullRequest);\n }\n const prMeetsRequiredApprovals = await approvalsSatisfied();\n if (!prMeetsRequiredApprovals) {\n await createPrComment({ body: 'PRs must meet all required approvals before entering the merge queue.' });\n return removePrFromQueue(pullRequest);\n }\n const queuedPrs = await getQueuedPullRequests();\n const queuePosition = queuedPrs.length;\n if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) {\n return updateMergeQueue(queuedPrs);\n }\n if (!pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))) {\n await addPrToQueue(pullRequest, queuePosition, skip_auto_merge);\n }\n\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n\n if (isFirstQueuePosition) {\n await updatePrWithDefaultBranch(pullRequest);\n }\n\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: isFirstQueuePosition ? 'success' : 'pending',\n description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.'\n });\n\n if (isFirstQueuePosition && slack_webhook_url && login) {\n await notifyUser({\n login,\n pull_number: context.issue.number,\n slack_webhook_url\n });\n }\n};\n\nexport const removePrFromQueue = async (pullRequest: PullRequest) => {\n const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n if (queueLabel) {\n await map([READY_FOR_MERGE_PR_LABEL, queueLabel], async label => await removeLabelIfExists(label, pullRequest.number));\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is not in the merge queue.'\n });\n const queuedPrs = await getQueuedPullRequests();\n return updateMergeQueue(queuedPrs);\n }\n};\n\nconst addPrToQueue = async (pullRequest: PullRequest, queuePosition: number, skip_auto_merge?: string) => {\n await octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number: context.issue.number,\n ...context.repo\n });\n if (skip_auto_merge == 'true') {\n core.info('Skipping auto merge per configuration.');\n return;\n }\n await enableAutoMerge(pullRequest.node_id);\n};\n\nconst getQueuedPullRequests = async (): Promise => {\n const openPullRequests = await paginateAllOpenPullRequests();\n return openPullRequests.filter(pr => pr.labels.some(label => label.name === READY_FOR_MERGE_PR_LABEL));\n};\n\nexport const enableAutoMerge = async (pullRequestId: string, mergeMethod = 'SQUASH') => {\n try {\n await octokitGraphql(`\n mutation {\n enablePullRequestAutoMerge(input: { pullRequestId: \"${pullRequestId}\", mergeMethod: ${mergeMethod} }) {\n clientMutationId\n }\n }\n `);\n } catch (error) {\n core.warning('Auto merge could not be enabled. Perhaps you need to enable auto-merge on your repo?');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { FIRST_QUEUED_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL, READY_FOR_MERGE_PR_LABEL } from '../constants';\nimport { GithubError, PullRequest, PullRequestList, SinglePullRequest } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { removePrFromQueue } from './manage-merge-queue';\n\nexport const prepareQueuedPrForMerge = async () => {\n const { data } = await octokit.pulls.list({\n state: 'open',\n per_page: 100,\n ...context.repo\n });\n const pullRequest = findNextPrToMerge(data);\n if (pullRequest) {\n return updatePrWithDefaultBranch(pullRequest as PullRequest);\n }\n};\n\nconst findNextPrToMerge = (pullRequests: PullRequestList) =>\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL])) ??\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, FIRST_QUEUED_PR_LABEL]));\n\nconst hasRequiredLabels = (pr: SinglePullRequest, requiredLabels: string[]) =>\n requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel));\n\nexport const updatePrWithDefaultBranch = async (pullRequest: PullRequest) => {\n if (pullRequest.head.user?.login && pullRequest.base.user?.login && pullRequest.head.user?.login !== pullRequest.base.user?.login) {\n try {\n // update fork default branch with upstream\n await octokit.repos.mergeUpstream({\n ...context.repo,\n branch: pullRequest.base.repo.default_branch\n });\n } catch (error) {\n if ((error as GithubError).status === 409) {\n core.setFailed('Attempt to update fork branch with upstream failed; conflict on default branch between fork and upstream.');\n } else core.setFailed((error as GithubError).message);\n }\n }\n try {\n await octokit.repos.merge({\n base: pullRequest.head.ref,\n head: 'HEAD',\n ...context.repo\n });\n } catch (error) {\n const noEvictUponConflict = core.getBooleanInput('no_evict_upon_conflict');\n if ((error as GithubError).status === 409) {\n if (!noEvictUponConflict) await removePrFromQueue(pullRequest);\n core.setFailed('The first PR in the queue has a merge conflict.');\n } else core.setFailed((error as GithubError).message);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { GithubError } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class RemoveLabel extends HelperInputs {\n label = '';\n}\n\nexport const removeLabel = async ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = async (labelName: string, issue_number: number) => {\n try {\n await octokit.issues.removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n });\n } catch (error) {\n if ((error as GithubError).status === 404) {\n core.info('Label is not present on PR.');\n }\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { PipelineState } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\nexport class SetCommitStatus extends HelperInputs {\n sha = '';\n context = '';\n state = '';\n description?: string;\n target_url?: string;\n skip_if_already_set?: string;\n}\n\nexport const setCommitStatus = async ({ sha, context, state, description, target_url, skip_if_already_set }: SetCommitStatus) => {\n await map(context.split('\\n').filter(Boolean), async context => {\n if (skip_if_already_set === 'true') {\n const check_runs = await octokit.checks.listForRef({\n ...githubContext.repo,\n ref: sha\n });\n const run = check_runs.data.check_runs.find(({ name }) => name === context);\n const runCompletedAndIsValid = run?.status === 'completed' && (run?.conclusion === 'failure' || run?.conclusion === 'success');\n if (runCompletedAndIsValid) {\n core.info(`${context} already completed with a ${run.conclusion} conclusion.`);\n return;\n }\n }\n\n octokit.repos.createCommitStatus({\n sha,\n context,\n state: state as PipelineState,\n description,\n target_url,\n ...githubContext.repo\n });\n });\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const convertToTeamSlug = (codeOwner: string) => codeOwner.substring(codeOwner.indexOf('/') + 1);\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { ChangedFilesList } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport const getChangedFilepaths = async (pull_number: number, ignore_deleted?: boolean) => {\n const changedFiles = await paginateAllChangedFilepaths(pull_number);\n const filesToMap = ignore_deleted ? changedFiles.filter(file => file.status !== 'removed') : changedFiles;\n return filesToMap.map(file => file.filename);\n};\n\nconst paginateAllChangedFilepaths = async (pull_number: number, page = 1): Promise => {\n const response = await octokit.pulls.listFiles({\n pull_number,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllChangedFilepaths(pull_number, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { CodeOwnersEntry, loadOwners, matchFile } from 'codeowners-utils';\nimport { uniq, union } from 'lodash';\nimport { context } from '@actions/github';\nimport { getChangedFilepaths } from './get-changed-filepaths';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { convertToTeamSlug } from './convert-to-team-slug';\n\nexport const getCoreMemberLogins = async (pull_number: number, teams?: string[]) => {\n const codeOwners = teams ?? getCodeOwnersFromEntries(await getRequiredCodeOwnersEntries(pull_number));\n const teamsAndLogins = await getCoreTeamsAndLogins(codeOwners);\n return uniq(teamsAndLogins.map(({ login }) => login));\n};\n\nexport const getRequiredCodeOwnersEntries = async (pull_number: number): Promise => {\n const codeOwners = (await loadOwners(process.cwd())) ?? [];\n const changedFilePaths = await getChangedFilepaths(pull_number);\n return changedFilePaths.map(filePath => matchFile(filePath, codeOwners)).filter(Boolean);\n};\n\nconst getCoreTeamsAndLogins = async (codeOwners?: string[]) => {\n if (!codeOwners?.length) {\n core.setFailed('No code owners found. Please provide a \"teams\" input or set up a CODEOWNERS file in your repo.');\n throw new Error();\n }\n\n const teamsAndLogins = await map(codeOwners, async team =>\n octokit.teams\n .listMembersInOrg({\n org: context.repo.owner,\n team_slug: team,\n per_page: 100\n })\n .then(listMembersResponse => listMembersResponse.data.map(({ login }) => ({ team, login })))\n );\n return union(...teamsAndLogins);\n};\n\nconst getCodeOwnersFromEntries = (codeOwnersEntries: CodeOwnersEntry[]) => {\n return uniq(\n codeOwnersEntries\n .map(entry => entry.owners)\n .flat()\n .filter(Boolean)\n .map(codeOwner => convertToTeamSlug(codeOwner))\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport axios from 'axios';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface NotifyUser {\n login: string;\n pull_number: number;\n slack_webhook_url: string;\n}\n\nexport const notifyUser = async ({ login, pull_number, slack_webhook_url }: NotifyUser) => {\n core.info(`Notifying user ${login}...`);\n const {\n data: { email }\n } = await octokit.users.getByUsername({ username: login });\n if (!email) {\n core.info(`No github email found for user ${login}. Ensure you have set your email to be publicly visible on your Github profile.`);\n return;\n }\n const {\n data: { title, html_url }\n } = await octokit.pulls.get({ pull_number, ...context.repo });\n\n try {\n await axios.post(slack_webhook_url, {\n assignee: email,\n title,\n html_url,\n repo: context.repo.repo\n });\n } catch (error) {\n core.warning('User notification failed');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllOpenPullRequests = async (page = 1): Promise => {\n const response = await octokit.pulls.list({\n state: 'open',\n sort: 'updated',\n direction: 'desc',\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllOpenPullRequests(page + 1));\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"676.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3DA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AC5BA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAMA;AAEA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;AC1HA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AAMA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;ACvFA;;;;;;;;;;;AAWA;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;;;AAIA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;ACjIA;;;;;;;;;;;AAWA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAAA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AClEA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;ACrCA;;;;;;;;;;;AAWA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;ACrDA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AAiDA;;;;;;;;;;;AC9DA;;;;;;;;;;;AAWA;AAEA;;;;;;;;;;;;;;ACbA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;AC5DA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AAQA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChDA;;;;;;;;;;;AAWA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":[".././src/constants.ts",".././src/utils/paginate-all-reviews.ts",".././src/helpers/approvals-satisfied.ts",".././src/helpers/create-pr-comment.ts",".././src/utils/update-merge-queue.ts",".././src/helpers/manage-merge-queue.ts",".././src/helpers/prepare-queued-pr-for-merge.ts",".././src/helpers/remove-label.ts",".././src/helpers/set-commit-status.ts",".././src/octokit.ts",".././src/types/generated.ts",".././src/utils/convert-to-team-slug.ts",".././src/utils/get-changed-filepaths.ts",".././src/utils/get-core-member-logins.ts",".././src/utils/notify-user.ts",".././src/utils/paginate-open-pull-requests.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// These extra headers are for experimental API features on Github Enterprise. See https://docs.github.com/en/enterprise-server@3.0/rest/overview/api-previews for details.\nconst PREVIEWS = ['ant-man', 'flash', 'groot', 'inertia', 'starfox'];\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: PREVIEWS.map(preview => `application/vnd.github.${preview}-preview+json`).join()\n }\n};\n\nexport const SECONDS_IN_A_DAY = 86400000;\nexport const DEFAULT_EXEMPT_DESCRIPTION = 'Passed in case the check is exempt.';\nexport const DEFAULT_PIPELINE_STATUS = 'Pipeline Status';\nexport const DEFAULT_PIPELINE_DESCRIPTION = 'Pipeline clear.';\nexport const PRODUCTION_ENVIRONMENT = 'production';\nexport const LATE_REVIEW = 'Late Review';\nexport const OVERDUE_ISSUE = 'Overdue';\nexport const ALMOST_OVERDUE_ISSUE = 'Due Soon';\nexport const PRIORITY_1 = 'Priority: Critical';\nexport const PRIORITY_2 = 'Priority: High';\nexport const PRIORITY_3 = 'Priority: Medium';\nexport const PRIORITY_4 = 'Priority: Low';\nexport const PRIORITY_LABELS = [PRIORITY_1, PRIORITY_2, PRIORITY_3, PRIORITY_4] as const;\nexport const PRIORITY_TO_DAYS_MAP = {\n [PRIORITY_1]: 2,\n [PRIORITY_2]: 14,\n [PRIORITY_3]: 45,\n [PRIORITY_4]: 90\n};\nexport const CORE_APPROVED_PR_LABEL = 'CORE APPROVED';\nexport const PEER_APPROVED_PR_LABEL = 'PEER APPROVED';\nexport const READY_FOR_MERGE_PR_LABEL = 'READY FOR MERGE';\nexport const MERGE_QUEUE_STATUS = 'QUEUE CHECKER';\nexport const QUEUED_FOR_MERGE_PREFIX = 'QUEUED FOR MERGE';\nexport const FIRST_QUEUED_PR_LABEL = `${QUEUED_FOR_MERGE_PREFIX} #1`;\nexport const JUMP_THE_QUEUE_PR_LABEL = 'JUMP THE QUEUE';\nexport const DEFAULT_PR_TITLE_REGEX = '^(build|ci|chore|docs|feat|fix|perf|refactor|style|test|revert|Revert|BREAKING CHANGE)((.*))?: .+$';\nexport const COPYRIGHT_HEADER = `/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/`;\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestReviewList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllReviews = async (prNumber: number, page = 1): Promise => {\n const response = await octokit.pulls.listReviews({\n pull_number: prNumber,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllReviews(prNumber, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { getRequiredCodeOwnersEntries } from '../utils/get-core-member-logins';\nimport { map } from 'bluebird';\nimport { convertToTeamSlug } from '../utils/convert-to-team-slug';\nimport { CodeOwnersEntry } from 'codeowners-utils';\nimport * as core from '@actions/core';\nimport { paginateAllReviews } from '../utils/paginate-all-reviews';\nimport { uniq, uniqBy } from 'lodash';\n\nexport class ApprovalsSatisfied extends HelperInputs {\n teams?: string;\n users?: string;\n number_of_reviewers?: string;\n required_review_overrides?: string;\n pull_number?: string;\n}\n\nexport const approvalsSatisfied = async ({\n teams,\n users,\n number_of_reviewers = '1',\n required_review_overrides,\n pull_number\n}: ApprovalsSatisfied = {}) => {\n const prNumber = pull_number ? Number(pull_number) : context.issue.number;\n\n const teamOverrides = required_review_overrides?.split(',').map(overrideString => {\n const [team, numberOfRequiredReviews] = overrideString.split(':');\n return { team, numberOfRequiredReviews };\n });\n const teamsList = updateTeamsList(teams?.split('\\n'));\n if (!validateTeamsList(teamsList)) {\n core.setFailed('If teams input is in the format \"org/team\", then the org must be the same as the repository org');\n return false;\n }\n const usersList = users?.split('\\n');\n\n const reviews = await paginateAllReviews(prNumber);\n const approverLogins = reviews\n .filter(({ state }) => state === 'APPROVED')\n .map(({ user }) => user?.login)\n .filter(Boolean);\n core.info(`PR already approved by: ${approverLogins.toString()}`);\n\n const requiredCodeOwnersEntries =\n teamsList || usersList\n ? createArtificialCodeOwnersEntry({ teams: teamsList, users: usersList })\n : await getRequiredCodeOwnersEntries(prNumber);\n const requiredCodeOwnersEntriesWithOwners = uniqBy(\n requiredCodeOwnersEntries.filter(({ owners }) => owners.length),\n 'owners'\n );\n\n const codeOwnersEntrySatisfiesApprovals = async (entry: Pick) => {\n const loginsLists = await map(entry.owners, async teamOrUsers => {\n if (isTeam(teamOrUsers)) {\n return await fetchTeamLogins(teamOrUsers);\n } else {\n return teamOrUsers.replaceAll('@', '').split(',');\n }\n });\n const codeOwnerLogins = uniq(loginsLists.flat());\n\n const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length;\n\n const numberOfRequiredReviews =\n teamOverrides?.find(({ team }) => team && entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers;\n core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`);\n core.info(`Number of required reviews: ${numberOfRequiredReviews}`);\n\n return numberOfApprovals >= Number(numberOfRequiredReviews);\n };\n\n core.info(`Required code owners: ${requiredCodeOwnersEntriesWithOwners.map(({ owners }) => owners).toString()}`);\n const booleans = await Promise.all(requiredCodeOwnersEntriesWithOwners.map(codeOwnersEntrySatisfiesApprovals));\n return booleans.every(Boolean);\n};\n\nconst createArtificialCodeOwnersEntry = ({ teams = [], users = [] }: { teams?: string[]; users?: string[] }) => [\n { owners: teams.concat(users) }\n];\nconst isTeam = (teamOrUsers: string) => teamOrUsers.includes('/');\nconst fetchTeamLogins = async (team: string) => {\n const { data } = await octokit.teams.listMembersInOrg({\n org: context.repo.owner,\n team_slug: convertToTeamSlug(team),\n per_page: 100\n });\n return data.map(({ login }) => login);\n};\nconst updateTeamsList = (teamsList?: string[]) => {\n return teamsList?.map(team => {\n if (!team.includes('/')) {\n return `${context.repo.owner}/${team}`;\n } else {\n return team;\n }\n });\n};\n\nconst validateTeamsList = (teamsList?: string[]) => {\n return (\n teamsList?.every(team => {\n const inputOrg = team.split('/')[0];\n return inputOrg === context.repo.owner;\n }) ?? true\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { GITHUB_OPTIONS } from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class CreatePrComment extends HelperInputs {\n body = '';\n sha?: string;\n login?: string;\n pull_number?: string;\n repo_name?: string;\n repo_owner_name?: string;\n}\n\nconst emptyResponse = { data: [] };\n\nconst getFirstPrByCommit = async (sha?: string, repo_name?: string, repo_owner_name?: string) => {\n const prs =\n (sha &&\n (await octokit.repos.listPullRequestsAssociatedWithCommit({\n commit_sha: sha,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner,\n ...GITHUB_OPTIONS\n }))) ||\n emptyResponse;\n\n return prs.data.find(Boolean)?.number;\n};\n\nconst getCommentByUser = async (login?: string, pull_number?: string, repo_name?: string, repo_owner_name?: string) => {\n const comments =\n (login &&\n (await octokit.issues.listComments({\n issue_number: pull_number ? Number(pull_number) : context.issue.number,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n }))) ||\n emptyResponse;\n\n return comments.data.find(comment => comment?.user?.login === login)?.id;\n};\n\nexport const createPrComment = async ({ body, sha, login, pull_number, repo_name, repo_owner_name }: CreatePrComment) => {\n const defaultPrNumber = context.issue.number;\n\n if (!sha && !login) {\n return octokit.issues.createComment({\n body,\n issue_number: pull_number ? Number(pull_number) : defaultPrNumber,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n }\n\n const prNumber = (await getFirstPrByCommit(sha, repo_name, repo_owner_name)) ?? (pull_number ? Number(pull_number) : defaultPrNumber);\n const commentId = await getCommentByUser(login, pull_number, repo_name, repo_owner_name);\n\n if (commentId) {\n return octokit.issues.updateComment({\n comment_id: commentId,\n body,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n } else {\n return octokit.issues.createComment({\n body,\n issue_number: prNumber,\n repo: repo_name ?? context.repo.repo,\n owner: repo_owner_name ?? context.repo.owner\n });\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants';\nimport { PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { updatePrWithDefaultBranch } from '../helpers/prepare-queued-pr-for-merge';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestList) => {\n const sortedPrs = sortPrsByQueuePosition(queuedPrs);\n return map(sortedPrs, updateQueuePosition);\n};\n\nconst sortPrsByQueuePosition = (queuedPrs: PullRequestList) =>\n queuedPrs\n .map(pr => {\n const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL));\n const queuePosition = isJumpingTheQueue ? 0 : Number(label?.split('#')?.[1]);\n return {\n number: pr.number,\n label,\n queuePosition,\n sha: pr.head.sha\n };\n })\n .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition);\n\nconst updateQueuePosition = async (pr: ReturnType[number], index: number) => {\n const { number, label, queuePosition, sha } = pr;\n const newQueuePosition = index + 1;\n if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) {\n return;\n }\n const prIsNowFirstInQueue = newQueuePosition === 1;\n if (prIsNowFirstInQueue) {\n const { data: firstPrInQueue } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithDefaultBranch(firstPrInQueue)]);\n const {\n data: {\n head: { sha: updatedHeadSha }\n }\n } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number),\n setCommitStatus({\n sha: updatedHeadSha,\n context: MERGE_QUEUE_STATUS,\n state: 'success',\n description: 'This PR is next to merge.'\n })\n ]);\n }\n\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number),\n setCommitStatus({\n sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is in line to merge.'\n })\n ]);\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport {\n FIRST_QUEUED_PR_LABEL,\n JUMP_THE_QUEUE_PR_LABEL,\n MERGE_QUEUE_STATUS,\n QUEUED_FOR_MERGE_PREFIX,\n READY_FOR_MERGE_PR_LABEL\n} from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { notifyUser } from '../utils/notify-user';\nimport { octokit, octokitGraphql } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\nimport { updatePrWithDefaultBranch } from './prepare-queued-pr-for-merge';\nimport { approvalsSatisfied } from './approvals-satisfied';\nimport { createPrComment } from './create-pr-comment';\n\nexport class ManageMergeQueue extends HelperInputs {\n login?: string;\n slack_webhook_url?: string;\n skip_auto_merge?: string;\n}\n\nexport const manageMergeQueue = async ({ login, slack_webhook_url, skip_auto_merge }: ManageMergeQueue = {}) => {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: context.issue.number, ...context.repo });\n if (pullRequest.merged || !pullRequest.labels.find(label => label.name === READY_FOR_MERGE_PR_LABEL)) {\n core.info('This PR is not in the merge queue.');\n return removePrFromQueue(pullRequest);\n }\n const prMeetsRequiredApprovals = await approvalsSatisfied();\n if (!prMeetsRequiredApprovals) {\n await createPrComment({ body: 'PRs must meet all required approvals before entering the merge queue.' });\n return removePrFromQueue(pullRequest);\n }\n const queuedPrs = await getQueuedPullRequests();\n const queuePosition = queuedPrs.length;\n if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) {\n return updateMergeQueue(queuedPrs);\n }\n if (!pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))) {\n await addPrToQueue(pullRequest, queuePosition, skip_auto_merge);\n }\n\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n\n if (isFirstQueuePosition) {\n await updatePrWithDefaultBranch(pullRequest);\n }\n\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: isFirstQueuePosition ? 'success' : 'pending',\n description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.'\n });\n\n if (isFirstQueuePosition && slack_webhook_url && login) {\n await notifyUser({\n login,\n pull_number: context.issue.number,\n slack_webhook_url\n });\n }\n};\n\nexport const removePrFromQueue = async (pullRequest: PullRequest) => {\n const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n if (queueLabel) {\n await map([READY_FOR_MERGE_PR_LABEL, queueLabel], async label => await removeLabelIfExists(label, pullRequest.number));\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is not in the merge queue.'\n });\n const queuedPrs = await getQueuedPullRequests();\n return updateMergeQueue(queuedPrs);\n }\n};\n\nconst addPrToQueue = async (pullRequest: PullRequest, queuePosition: number, skip_auto_merge?: string) => {\n await octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number: context.issue.number,\n ...context.repo\n });\n if (skip_auto_merge == 'true') {\n core.info('Skipping auto merge per configuration.');\n return;\n }\n await enableAutoMerge(pullRequest.node_id);\n};\n\nconst getQueuedPullRequests = async (): Promise => {\n const openPullRequests = await paginateAllOpenPullRequests();\n return openPullRequests.filter(pr => pr.labels.some(label => label.name === READY_FOR_MERGE_PR_LABEL));\n};\n\nexport const enableAutoMerge = async (pullRequestId: string, mergeMethod = 'SQUASH') => {\n try {\n await octokitGraphql(`\n mutation {\n enablePullRequestAutoMerge(input: { pullRequestId: \"${pullRequestId}\", mergeMethod: ${mergeMethod} }) {\n clientMutationId\n }\n }\n `);\n } catch (error) {\n core.warning('Auto merge could not be enabled. Perhaps you need to enable auto-merge on your repo?');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { FIRST_QUEUED_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL, READY_FOR_MERGE_PR_LABEL } from '../constants';\nimport { GithubError, PullRequest, PullRequestList, SinglePullRequest } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { removePrFromQueue } from './manage-merge-queue';\n\nexport const prepareQueuedPrForMerge = async () => {\n const { data } = await octokit.pulls.list({\n state: 'open',\n per_page: 100,\n ...context.repo\n });\n const pullRequest = findNextPrToMerge(data);\n if (pullRequest) {\n return updatePrWithDefaultBranch(pullRequest as PullRequest);\n }\n};\n\nconst findNextPrToMerge = (pullRequests: PullRequestList) =>\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL])) ??\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, FIRST_QUEUED_PR_LABEL]));\n\nconst hasRequiredLabels = (pr: SinglePullRequest, requiredLabels: string[]) =>\n requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel));\n\nexport const updatePrWithDefaultBranch = async (pullRequest: PullRequest) => {\n if (pullRequest.head.user?.login && pullRequest.base.user?.login && pullRequest.head.user?.login !== pullRequest.base.user?.login) {\n try {\n // update fork default branch with upstream\n await octokit.repos.mergeUpstream({\n ...context.repo,\n branch: pullRequest.base.repo.default_branch\n });\n } catch (error) {\n if ((error as GithubError).status === 409) {\n core.setFailed('Attempt to update fork branch with upstream failed; conflict on default branch between fork and upstream.');\n } else core.setFailed((error as GithubError).message);\n }\n }\n try {\n await octokit.repos.merge({\n base: pullRequest.head.ref,\n head: 'HEAD',\n ...context.repo\n });\n } catch (error) {\n const noEvictUponConflict = core.getBooleanInput('no_evict_upon_conflict');\n if ((error as GithubError).status === 409) {\n if (!noEvictUponConflict) await removePrFromQueue(pullRequest);\n core.setFailed('The first PR in the queue has a merge conflict.');\n } else core.setFailed((error as GithubError).message);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { GithubError } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class RemoveLabel extends HelperInputs {\n label = '';\n}\n\nexport const removeLabel = async ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = async (labelName: string, issue_number: number) => {\n try {\n await octokit.issues.removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n });\n } catch (error) {\n if ((error as GithubError).status === 404) {\n core.info('Label is not present on PR.');\n }\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { PipelineState } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\nexport class SetCommitStatus extends HelperInputs {\n sha = '';\n context = '';\n state = '';\n description?: string;\n target_url?: string;\n skip_if_already_set?: string;\n}\n\nexport const setCommitStatus = async ({ sha, context, state, description, target_url, skip_if_already_set }: SetCommitStatus) => {\n await map(context.split('\\n').filter(Boolean), async context => {\n if (skip_if_already_set === 'true') {\n const check_runs = await octokit.checks.listForRef({\n ...githubContext.repo,\n ref: sha\n });\n const run = check_runs.data.check_runs.find(({ name }) => name === context);\n const runCompletedAndIsValid = run?.status === 'completed' && (run?.conclusion === 'failure' || run?.conclusion === 'success');\n if (runCompletedAndIsValid) {\n core.info(`${context} already completed with a ${run.conclusion} conclusion.`);\n return;\n }\n }\n\n octokit.repos.createCommitStatus({\n sha,\n context,\n state: state as PipelineState,\n description,\n target_url,\n ...githubContext.repo\n });\n });\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport const convertToTeamSlug = (codeOwner: string) => codeOwner.substring(codeOwner.indexOf('/') + 1);\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { ChangedFilesList } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport const getChangedFilepaths = async (pull_number: number, ignore_deleted?: boolean) => {\n const changedFiles = await paginateAllChangedFilepaths(pull_number);\n const filesToMap = ignore_deleted ? changedFiles.filter(file => file.status !== 'removed') : changedFiles;\n return filesToMap.map(file => file.filename);\n};\n\nconst paginateAllChangedFilepaths = async (pull_number: number, page = 1): Promise => {\n const response = await octokit.pulls.listFiles({\n pull_number,\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllChangedFilepaths(pull_number, page + 1));\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { CodeOwnersEntry, loadOwners, matchFile } from 'codeowners-utils';\nimport { uniq, union } from 'lodash';\nimport { context } from '@actions/github';\nimport { getChangedFilepaths } from './get-changed-filepaths';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { convertToTeamSlug } from './convert-to-team-slug';\n\nexport const getCoreMemberLogins = async (pull_number: number, teams?: string[]) => {\n const codeOwners = teams ?? getCodeOwnersFromEntries(await getRequiredCodeOwnersEntries(pull_number));\n const teamsAndLogins = await getCoreTeamsAndLogins(codeOwners);\n return uniq(teamsAndLogins.map(({ login }) => login));\n};\n\nexport const getRequiredCodeOwnersEntries = async (pull_number: number): Promise => {\n const codeOwners = (await loadOwners(process.cwd())) ?? [];\n const changedFilePaths = await getChangedFilepaths(pull_number);\n return changedFilePaths.map(filePath => matchFile(filePath, codeOwners)).filter(Boolean);\n};\n\nconst getCoreTeamsAndLogins = async (codeOwners?: string[]) => {\n if (!codeOwners?.length) {\n core.setFailed('No code owners found. Please provide a \"teams\" input or set up a CODEOWNERS file in your repo.');\n throw new Error();\n }\n\n const teamsAndLogins = await map(codeOwners, async team =>\n octokit.teams\n .listMembersInOrg({\n org: context.repo.owner,\n team_slug: team,\n per_page: 100\n })\n .then(listMembersResponse => listMembersResponse.data.map(({ login }) => ({ team, login })))\n );\n return union(...teamsAndLogins);\n};\n\nconst getCodeOwnersFromEntries = (codeOwnersEntries: CodeOwnersEntry[]) => {\n return uniq(\n codeOwnersEntries\n .map(entry => entry.owners)\n .flat()\n .filter(Boolean)\n .map(codeOwner => convertToTeamSlug(codeOwner))\n );\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport axios from 'axios';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface NotifyUser {\n login: string;\n pull_number: number;\n slack_webhook_url: string;\n}\n\nexport const notifyUser = async ({ login, pull_number, slack_webhook_url }: NotifyUser) => {\n core.info(`Notifying user ${login}...`);\n const {\n data: { email }\n } = await octokit.users.getByUsername({ username: login });\n if (!email) {\n core.info(`No github email found for user ${login}. Ensure you have set your email to be publicly visible on your Github profile.`);\n return;\n }\n const {\n data: { title, html_url }\n } = await octokit.pulls.get({ pull_number, ...context.repo });\n\n try {\n await axios.post(slack_webhook_url, {\n assignee: email,\n title,\n html_url,\n repo: context.repo.repo\n });\n } catch (error) {\n core.warning('User notification failed');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllOpenPullRequests = async (page = 1): Promise => {\n const response = await octokit.pulls.list({\n state: 'open',\n sort: 'updated',\n direction: 'desc',\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllOpenPullRequests(page + 1));\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/dist/839.index.js b/dist/839.index.js index c7dbbadd..589c4521 100644 --- a/dist/839.index.js +++ b/dist/839.index.js @@ -49,6 +49,8 @@ const generateMatrix = ({ paths, batches: _batches = '1', load_balancing_sizes } let currentLoadSize = 0; let currentBatch = []; matrixValues.forEach((path, index) => { + if (!loadBalancingSizes[index]) + throw new Error('load_balancing_sizes input must contain values'); const possibleLoadSize = currentLoadSize + loadBalancingSizes[index]; if (Math.abs(possibleLoadSize - targetLoadSize) <= Math.abs(loadBalancingSizes[index] - targetLoadSize)) { currentLoadSize += loadBalancingSizes[index]; diff --git a/dist/839.index.js.map b/dist/839.index.js.map index ac24a6b8..0e05acf2 100644 --- a/dist/839.index.js.map +++ b/dist/839.index.js.map @@ -1 +1 @@ -{"version":3,"file":"839.index.js","mappings":";;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AAEA;AAAA;;AACA;AAGA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;ACzDA;;;;;;;;;;;AAWA;AAEA;AAiDA","sources":[".././src/helpers/generate-matrix.ts",".././src/types/generated.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { chunk, sum } from 'lodash';\n\nexport class GenerateMatrix extends HelperInputs {\n paths = '';\n batches?: string;\n load_balancing_sizes?: string;\n}\n\nexport const generateMatrix = ({ paths, batches: _batches = '1', load_balancing_sizes }: GenerateMatrix) => {\n const matrixValues = paths.split(/[\\n,]/);\n const batches = Number(_batches);\n if (!load_balancing_sizes || matrixValues.length <= batches) {\n return {\n include: chunk(matrixValues, Math.ceil(matrixValues.length / batches)).map(chunk => ({ path: chunk.join(',') }))\n };\n }\n const loadBalancingSizes = load_balancing_sizes.split(/[\\n,]/).map(size => Number(size));\n if (loadBalancingSizes.length !== matrixValues.length)\n throw new Error('load_balancing_sizes input must have the same length as paths input');\n const targetLoadSize = sum(loadBalancingSizes) / batches;\n const loadBalancedPaths: string[] = [];\n let currentLoadSize = 0;\n let currentBatch: string[] = [];\n matrixValues.forEach((path, index) => {\n const possibleLoadSize = currentLoadSize + loadBalancingSizes[index];\n if (Math.abs(possibleLoadSize - targetLoadSize) <= Math.abs(loadBalancingSizes[index] - targetLoadSize)) {\n currentLoadSize += loadBalancingSizes[index];\n currentBatch.push(path);\n } else {\n loadBalancedPaths.push(currentBatch.join(','));\n currentBatch = [path];\n currentLoadSize = loadBalancingSizes[index];\n }\n if (currentLoadSize >= targetLoadSize) {\n loadBalancedPaths.push(currentBatch.join(','));\n currentBatch = [];\n currentLoadSize = 0;\n }\n });\n if (currentBatch.length > 0) loadBalancedPaths.push(currentBatch.join(','));\n return {\n include: loadBalancedPaths.map(path => ({ path }))\n };\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"839.index.js","mappings":";;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AAEA;AAAA;;AACA;AAGA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;AC1DA;;;;;;;;;;;AAWA;AAEA;AAiDA","sources":[".././src/helpers/generate-matrix.ts",".././src/types/generated.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { chunk, sum } from 'lodash';\n\nexport class GenerateMatrix extends HelperInputs {\n paths = '';\n batches?: string;\n load_balancing_sizes?: string;\n}\n\nexport const generateMatrix = ({ paths, batches: _batches = '1', load_balancing_sizes }: GenerateMatrix) => {\n const matrixValues = paths.split(/[\\n,]/);\n const batches = Number(_batches);\n if (!load_balancing_sizes || matrixValues.length <= batches) {\n return {\n include: chunk(matrixValues, Math.ceil(matrixValues.length / batches)).map(chunk => ({ path: chunk.join(',') }))\n };\n }\n const loadBalancingSizes = load_balancing_sizes.split(/[\\n,]/).map(size => Number(size));\n if (loadBalancingSizes.length !== matrixValues.length)\n throw new Error('load_balancing_sizes input must have the same length as paths input');\n const targetLoadSize = sum(loadBalancingSizes) / batches;\n const loadBalancedPaths: string[] = [];\n let currentLoadSize = 0;\n let currentBatch: string[] = [];\n matrixValues.forEach((path, index) => {\n if (!loadBalancingSizes[index]) throw new Error('load_balancing_sizes input must contain values');\n const possibleLoadSize = currentLoadSize + loadBalancingSizes[index];\n if (Math.abs(possibleLoadSize - targetLoadSize) <= Math.abs(loadBalancingSizes[index] - targetLoadSize)) {\n currentLoadSize += loadBalancingSizes[index];\n currentBatch.push(path);\n } else {\n loadBalancedPaths.push(currentBatch.join(','));\n currentBatch = [path];\n currentLoadSize = loadBalancingSizes[index];\n }\n if (currentLoadSize >= targetLoadSize) {\n loadBalancedPaths.push(currentBatch.join(','));\n currentBatch = [];\n currentLoadSize = 0;\n }\n });\n if (currentBatch.length > 0) loadBalancedPaths.push(currentBatch.join(','));\n return {\n include: loadBalancedPaths.map(path => ({ path }))\n };\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n users?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n skip_auto_merge?: string;\n repo_name?: string;\n repo_owner_name?: string;\n load_balancing_sizes?: string;\n required_review_overrides?: string;\n}\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/src/helpers/approvals-satisfied.ts b/src/helpers/approvals-satisfied.ts index be351fb3..380ef8a1 100644 --- a/src/helpers/approvals-satisfied.ts +++ b/src/helpers/approvals-satisfied.ts @@ -79,7 +79,7 @@ export const approvalsSatisfied = async ({ const numberOfApprovals = approverLogins.filter(login => codeOwnerLogins.includes(login)).length; const numberOfRequiredReviews = - teamOverrides?.find(({ team }) => entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; + teamOverrides?.find(({ team }) => team && entry.owners.includes(team))?.numberOfRequiredReviews ?? number_of_reviewers; core.info(`Current number of approvals satisfied for ${entry.owners}: ${numberOfApprovals}`); core.info(`Number of required reviews: ${numberOfRequiredReviews}`); diff --git a/src/helpers/generate-matrix.ts b/src/helpers/generate-matrix.ts index 2f6b48d2..72673076 100644 --- a/src/helpers/generate-matrix.ts +++ b/src/helpers/generate-matrix.ts @@ -36,6 +36,7 @@ export const generateMatrix = ({ paths, batches: _batches = '1', load_balancing_ let currentLoadSize = 0; let currentBatch: string[] = []; matrixValues.forEach((path, index) => { + if (!loadBalancingSizes[index]) throw new Error('load_balancing_sizes input must contain values'); const possibleLoadSize = currentLoadSize + loadBalancingSizes[index]; if (Math.abs(possibleLoadSize - targetLoadSize) <= Math.abs(loadBalancingSizes[index] - targetLoadSize)) { currentLoadSize += loadBalancingSizes[index]; diff --git a/test/helpers/get-changed-files.test.ts b/test/helpers/get-changed-files.test.ts index 39e00b5a..6868cd1b 100644 --- a/test/helpers/get-changed-files.test.ts +++ b/test/helpers/get-changed-files.test.ts @@ -95,13 +95,13 @@ describe('getChangedFiles', () => { it('case where one of the file paths match the file paths that octokit returns', async () => { const result = await getChangedFiles({}); - expect(result).toEqual(`${mock_data1[0].filename},${mock_data1[1].filename},${mock_data2[0].filename}`); + expect(result).toEqual(`${mock_data1[0]!.filename},${mock_data1[1]!.filename},${mock_data2[0]!.filename}`); }); it('case where files returned from getChangedFiles matches the provided regex pattern', async () => { const result = await getChangedFiles({ pattern: 'file/path/1/file[0-9].txt' }); - expect(result).toEqual(`${mock_data1[0].filename}`); + expect(result).toEqual(`${mock_data1[0]!.filename}`); }); it('case where files returned from getChangedFiles does not match the provided regex pattern', async () => { @@ -116,6 +116,6 @@ describe('getChangedFiles', () => { })); const result = await getChangedFiles({ ignore_deleted: 'true' }); - expect(result).toEqual(mock_data3[0].filename); + expect(result).toEqual(mock_data3[0]!.filename); }); }); diff --git a/tsconfig.json b/tsconfig.json index a5ec7f0b..de3452db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "noEmit": true, "strict": true, "noImplicitAny": true, + "noUncheckedIndexedAccess": true, "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true,