backport
Version:
A CLI tool that automates the process of backporting commits
115 lines • 5.31 kB
JavaScript
/** Per-branch backport: cherry-pick commits, resolve conflicts, create target PR, add labels/assignees/reviewers. */
import chalk from 'chalk';
import { flatten } from 'lodash-es';
import { getSourceBranchFromCommits } from '../get-source-branch-from-commits.js';
import { createBackportBranch, deleteBackportBranch, pushBackportBranch, getRepoForkOwner, } from '../git/index.js';
import { addAssigneesToPullRequest } from '../github/v3/add-assignees-to-pull-request.js';
import { addLabelsToPullRequest } from '../github/v3/add-labels-to-pull-request.js';
import { addReviewersToPullRequest } from '../github/v3/add-reviewers-to-pull-request.js';
import { createPullRequest } from '../github/v3/create-pull-request/create-pull-request.js';
import { getPullRequestBody } from '../github/v3/create-pull-request/get-pull-request-body.js';
import { getTitle } from '../github/v3/create-pull-request/get-title.js';
import { validateTargetBranch } from '../github/v4/validate-target-branch.js';
import { consoleLog } from '../logger.js';
import { sequentially } from '../sequential-helper.js';
import { autoMergeNowOrLater } from './auto-merge-now-or-later.js';
import { copySourcePullRequestReviewersToTargetPullRequest } from './copy-source-pull-request-reviewers-to-target-pull-request.js';
import { getBackportBranchName } from './get-backport-branch-name.js';
import { getMergeCommits } from './get-merge-commit.js';
import { getTargetPRLabels } from './getTargetPRLabels/get-target-prlabels.js';
import { waitForCherrypick } from './wait-for-cherrypick.js';
export async function cherrypickAndCreateTargetPullRequest({ options, commits, targetBranch, }) {
const backportBranch = getBackportBranchName({
options,
targetBranch,
commits,
});
const repoForkOwner = getRepoForkOwner(options);
consoleLog(`\n${chalk.bold(`Backporting to ${targetBranch}:`)}`);
await validateTargetBranch({ ...options, branchName: targetBranch });
await createBackportBranch({
options,
sourceBranch: getSourceBranchFromCommits(commits),
targetBranch,
backportBranch,
});
const commitsFlattened = flatten(await Promise.all(commits.map((c) => getMergeCommits(options, c))));
const cherrypickResults = await sequentially(commitsFlattened, (commit) => waitForCherrypick(options, commit, targetBranch));
const hasAnyCommitWithConflicts = cherrypickResults.some((r) => r.hasCommitsWithConflicts);
const unresolvedFiles = cherrypickResults.flatMap((r) => r.unresolvedFiles);
if (!options.dryRun) {
await pushBackportBranch({ options, backportBranch });
await deleteBackportBranch({ options, backportBranch });
}
const prPayload = {
owner: options.repoOwner,
repo: options.repoName,
title: getTitle({ options, commits, targetBranch }),
body: getPullRequestBody({
options,
commits,
targetBranch,
hasAnyCommitWithConflicts,
unresolvedFiles,
}),
head: `${repoForkOwner}:${backportBranch}`, // eg. sorenlouv:backport/7.x/pr-75007
base: targetBranch, // eg. 7.x
draft: options.draft,
};
const targetPullRequest = await createPullRequest({ options, prPayload });
// add assignees to target pull request
const assignees = options.autoAssign
? [options.authenticatedUsername]
: options.assignees;
if (assignees.length > 0) {
await addAssigneesToPullRequest({
...options,
pullNumber: targetPullRequest.number,
assignees,
});
}
// add reviewers to target pull request
if (options.reviewers.length > 0) {
await addReviewersToPullRequest({
...options,
pullNumber: targetPullRequest.number,
reviewers: options.reviewers,
});
}
// add reviewers of the original PRs to the target pull request
if (options.copySourcePRReviewers) {
await copySourcePullRequestReviewersToTargetPullRequest(options, commits, targetPullRequest.number);
}
const targetPRLabels = getTargetPRLabels({
interactive: options.interactive,
targetPRLabels: options.targetPRLabels,
copySourcePRLabels: options.copySourcePRLabels,
commits,
targetBranch,
});
// add labels to target pull request
if (targetPRLabels.length > 0) {
await addLabelsToPullRequest({
...options,
pullNumber: targetPullRequest.number,
labels: targetPRLabels,
});
}
// make PR auto mergable
if (options.autoMerge && !hasAnyCommitWithConflicts) {
await autoMergeNowOrLater(options, targetPullRequest.number);
}
// add labels to source pull requests
if (options.sourcePRLabels.length > 0) {
await Promise.all(commits
.filter((commit) => commit.sourcePullRequest)
.map((commit) => addLabelsToPullRequest({
...options,
pullNumber: commit.sourcePullRequest.number,
labels: options.sourcePRLabels,
})));
}
consoleLog(`View pull request: ${targetPullRequest.url}`);
return targetPullRequest;
}
//# sourceMappingURL=cherrypick-and-create-target-pull-request.js.map