1/// <reference lib="esnext.asynciterable" /> 2// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally 3/// <reference types="node" /> 4 5import { Octokit } from "@octokit/rest"; 6const {runSequence} = require("./run-sequence"); 7import fs = require("fs"); 8import path = require("path"); 9 10const userName = process.env.GH_USERNAME; 11const reviewers = process.env.REQUESTING_USER ? [process.env.REQUESTING_USER] : ["weswigham", "RyanCavanaugh"]; 12const branchName = `pick/${process.env.SOURCE_ISSUE}/${process.env.TARGET_BRANCH}`; 13const remoteUrl = `https://${process.argv[2]}@github.com/${userName}/TypeScript.git`; 14const produceLKG = !!process.env.PRODUCE_LKG; 15 16async function main() { 17 if (!process.env.TARGET_BRANCH) { 18 throw new Error("Target branch not specified"); 19 } 20 if (!process.env.SOURCE_ISSUE) { 21 throw new Error("Source issue not specified"); 22 } 23 const currentSha = runSequence([ 24 ["git", ["rev-parse", "HEAD"]] 25 ]); 26 const currentAuthor = runSequence([ 27 ["git", ["log", "-1", `--pretty="%aN <%aE>"`]] 28 ]); 29 30 const gh = new Octokit({ 31 auth: process.argv[2] 32 }); 33 34 const inputPR = (await gh.pulls.get({ pull_number: +process.env.SOURCE_ISSUE, owner: "microsoft", repo: "TypeScript" })).data; 35 let remoteName = "origin"; 36 if (inputPR.base.repo.git_url !== `git:github.com/microsoft/TypeScript`) { 37 runSequence([ 38 ["git", ["remote", "add", "nonlocal", inputPR.base.repo.git_url]] 39 ]); 40 remoteName = "nonlocal"; 41 } 42 const baseBranchName = inputPR.base.ref; 43 runSequence([ 44 ["git", ["fetch", remoteName, baseBranchName]] 45 ]); 46 let logText = runSequence([ 47 ["git", ["log", `${remoteName}/${baseBranchName}..${currentSha.trim()}`, `--pretty="%h %s%n%b"`, "--reverse"]] 48 ]); 49 logText = `Cherry-pick PR #${process.env.SOURCE_ISSUE} into ${process.env.TARGET_BRANCH} 50 51Component commits: 52${logText.trim()}`; 53 const logpath = path.join(__dirname, "../", "logmessage.txt"); 54 const mergebase = runSequence([["git", ["merge-base", `${remoteName}/${baseBranchName}`, currentSha]]]).trim(); 55 runSequence([ 56 ["git", ["checkout", "-b", "temp-branch"]], 57 ["git", ["reset", mergebase, "--soft"]] 58 ]); 59 fs.writeFileSync(logpath, logText); 60 runSequence([ 61 ["git", ["commit", "-F", logpath, `--author="${currentAuthor.trim()}"`]] 62 ]); 63 fs.unlinkSync(logpath); 64 const squashSha = runSequence([ 65 ["git", ["rev-parse", "HEAD"]] 66 ]); 67 runSequence([ 68 ["git", ["checkout", process.env.TARGET_BRANCH]], // checkout the target branch 69 ["git", ["checkout", "-b", branchName]], // create a new branch 70 ["git", ["cherry-pick", squashSha.trim()]], 71 ]); 72 if (produceLKG) { 73 runSequence([ 74 ["gulp", ["LKG"]], 75 ["git", ["add", "lib"]], 76 ["git", ["commit", "-m", `"Update LKG"`]] 77 ]); 78 } 79 runSequence([ 80 ["git", ["remote", "add", "fork", remoteUrl]], // Add the remote fork 81 ["git", ["push", "--set-upstream", "fork", branchName, "-f"]] // push the branch 82 ]); 83 84 const r = await gh.pulls.create({ 85 owner: "Microsoft", 86 repo: "TypeScript", 87 maintainer_can_modify: true, 88 title: ` Pick PR #${process.env.SOURCE_ISSUE} (${inputPR.title.substring(0, 35)}${inputPR.title.length > 35 ? "..." : ""}) into ${process.env.TARGET_BRANCH}`, 89 head: `${userName}:${branchName}`, 90 base: process.env.TARGET_BRANCH, 91 body: 92 `This cherry-pick was triggered by a request on https://github.com/Microsoft/TypeScript/pull/${process.env.SOURCE_ISSUE} 93Please review the diff and merge if no changes are unexpected.${produceLKG ? ` An LKG update commit is included seperately from the base change.` : ""} 94You can view the cherry-pick log [here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary). 95 96cc ${reviewers.map(r => "@" + r).join(" ")}`, 97 }); 98 const num = r.data.number; 99 console.log(`Pull request ${num} created.`); 100 101 await gh.issues.createComment({ 102 issue_number: +process.env.SOURCE_ISSUE, 103 owner: "Microsoft", 104 repo: "TypeScript", 105 body: `Hey @${process.env.REQUESTING_USER}, I've opened #${num} for you.` 106 }); 107} 108 109main().catch(async e => { 110 console.error(e); 111 process.exitCode = 1; 112 if (process.env.SOURCE_ISSUE) { 113 const gh = new Octokit({ 114 auth: process.argv[2] 115 }); 116 await gh.issues.createComment({ 117 issue_number: +process.env.SOURCE_ISSUE, 118 owner: "Microsoft", 119 repo: "TypeScript", 120 body: `Hey @${process.env.REQUESTING_USER}, I couldn't open a PR with the cherry-pick. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${process.env.BUILD_BUILDID}&_a=summary)). You may need to squash and pick this PR into ${process.env.TARGET_BRANCH} manually.` 121 }); 122 } 123}); 124