• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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