UNPKG

backport

Version:

A CLI tool that automates the process of backporting commits

135 lines 5.89 kB
import { merge, keyBy, values, uniqBy } from 'lodash-es'; import { filterNil } from '../../utils/filter-empty.js'; import { getFirstLine } from '../github/commit-formatters.js'; import { getSourcePullRequest } from './get-source-pull-request.js'; import { isPullRequestCrossReferencedEvent } from './is-pull-request-cross-referenced-event.js'; function mergeByKey(obj1, obj2, key) { const merged = merge(keyBy(obj1, key), keyBy(obj2, key)); const a = values(merged); return a; } export function getPullRequestStates({ sourceCommit, branchLabelMapping, }) { const sourcePullRequest = getSourcePullRequest(sourceCommit); // if there is no source pull request the commit was pushed directly to the source branch // in that case there will be no labels, and thus not possible to deduce the expected target branches if (!sourcePullRequest) { return []; } const createdTargetPullRequests = getCreatedTargetPullRequests(sourceCommit); // if there's no `branchLabelMapping`, it's not possible to determine the missing target branches if (!branchLabelMapping) { return createdTargetPullRequests; } const targetBranchesFromLabels = getTargetBranchesFromLabels(sourcePullRequest, branchLabelMapping); const allTargetBranches = mergeByKey(targetBranchesFromLabels, createdTargetPullRequests, 'branch'); return allTargetBranches.map((res) => { if (res.state) { return { ...res, state: res.state }; } // MERGED (source branch) if (res.isSourceBranch) { return { ...res, state: 'MERGED', url: String(sourcePullRequest.url), number: sourcePullRequest.number, mergeCommit: sourcePullRequest.mergeCommit ? { message: sourcePullRequest.mergeCommit.message, sha: String(sourcePullRequest.mergeCommit.sha), } : undefined, }; } // NOT_CREATED return { ...res, state: 'NOT_CREATED' }; }); } function getCreatedTargetPullRequests(sourceCommit) { const sourcePullRequest = getSourcePullRequest(sourceCommit); if (!sourcePullRequest) { return []; } const sourceCommitMessage = getFirstLine(sourceCommit.message); return (sourcePullRequest.timelineItems.edges ?? []) .filter(filterNil) .filter(isPullRequestCrossReferencedEvent) .filter((item) => { const { targetPullRequest } = item.node; // ignore closed PRs if (targetPullRequest.state === 'CLOSED') { return false; } // at least one of the commits in `targetPullRequest` should match the merge commit from the source pull request const didCommitMatch = targetPullRequest.commits.edges?.some((commitEdge) => { const targetCommit = commitEdge?.node?.targetCommit; if (!targetCommit) { return false; } const matchingRepoName = sourceCommit.repository.name === targetPullRequest.repository.name; const matchingRepoOwner = sourceCommit.repository.owner.login === targetPullRequest.repository.owner.login; const targetCommitMessage = getFirstLine(targetCommit.message); const matchingMessage = targetCommitMessage === sourceCommitMessage; return matchingRepoName && matchingRepoOwner && matchingMessage; }); const titleIncludesMessage = targetPullRequest.title.includes(sourceCommitMessage); const titleIncludesNumber = targetPullRequest.title.includes(sourcePullRequest.number.toString()); if (didCommitMatch) { return true; } return titleIncludesMessage && titleIncludesNumber; }) .map((item) => { const { targetPullRequest } = item.node; return { url: String(targetPullRequest.url), number: targetPullRequest.number, branch: targetPullRequest.baseRefName, state: targetPullRequest.state, mergeCommit: targetPullRequest.targetMergeCommit ? { sha: String(targetPullRequest.targetMergeCommit.sha), message: targetPullRequest.targetMergeCommit.message, } : undefined, }; }); } function getTargetBranchesFromLabels(sourcePullRequest, branchLabelMapping) { const targetBranchesFromLabels = sourcePullRequest.labels?.nodes ?.map((label) => label?.name) .filter(filterNil) .map((label) => { const res = getTargetBranchFromLabel({ branchLabelMapping, label }); if (res) { const { branchLabelMappingKey, targetBranch } = res; const isSourceBranch = targetBranch === sourcePullRequest.baseRefName; return { branch: targetBranch, label, branchLabelMappingKey, isSourceBranch, }; } }) .filter(filterNil); return uniqBy(targetBranchesFromLabels, ({ branch }) => branch); } export function getTargetBranchFromLabel({ branchLabelMapping, label, }) { // only get first match const result = Object.entries(branchLabelMapping).find(([key]) => { const regex = new RegExp(key); const isMatch = label.match(regex) !== null; return isMatch; }); if (result) { const [branchLabelMappingKey, branchLabelMappingValue] = result; const regex = new RegExp(branchLabelMappingKey); const targetBranch = label.replace(regex, branchLabelMappingValue); if (targetBranch) { return { targetBranch, branchLabelMappingKey }; } } } //# sourceMappingURL=get-pull-request-states.js.map