|
| 1 | +/// <reference lib="esnext.asynciterable" /> |
| 2 | +// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally |
| 3 | +const cp = require("child_process"); |
| 4 | +const Octokit = require("@octokit/rest"); |
| 5 | +const { AllPackages, getDefinitelyTyped, loggerWithErrors, |
| 6 | + parseDefinitions, parseNProcesses, clean } = require("types-publisher"); |
| 7 | +const { writeFile } = require("fs-extra"); |
| 8 | + |
| 9 | +async function main() { |
| 10 | + const options = { definitelyTypedPath: ".", progress: false, parseInParallel: true }; |
| 11 | + const log = loggerWithErrors()[0]; |
| 12 | + |
| 13 | + clean(); |
| 14 | + const dt = await getDefinitelyTyped(options, log); |
| 15 | + await parseDefinitions(dt, { nProcesses: parseNProcesses(), definitelyTypedPath: "." }, log); |
| 16 | + const allPackages = await AllPackages.read(dt); |
| 17 | + const typings = allPackages.allTypings(); |
| 18 | + const maxPathLen = Math.max(...typings.map(t => t.subDirectoryPath.length)); |
| 19 | + const entries = mapDefined(typings, t => getEntry(t, maxPathLen)); |
| 20 | + await writeFile([options.definitelyTypedPath, ".github", "CODEOWNERS"].join("/"), `${header}\n\n${entries.join("\n")}\n`, { encoding: "utf-8" }); |
| 21 | +} |
| 22 | + |
| 23 | +const token = /** @type {string} */(process.env.GH_TOKEN); |
| 24 | +const gh = new Octokit(); |
| 25 | +const reviewers = ["weswigham", "sandersn", "RyanCavanaugh"] |
| 26 | +const now = new Date(); |
| 27 | +const branchName = `codeowner-update-${now.getFullYear()}${padNum(now.getMonth())}${padNum(now.getDay())}`; |
| 28 | +const remoteUrl = `https://${token}@github.com/DefinitelyTyped/DefinitelyTyped.git`; |
| 29 | +runSequence([ |
| 30 | + ["git", ["checkout", "."]], // reset any changes |
| 31 | +]); |
| 32 | + |
| 33 | +main().then(() => { |
| 34 | + runSequence([ |
| 35 | + ["git", ["checkout", "-b", branchName]], // create a branch |
| 36 | + ["git", ["add", ".github/CODEOWNERS"]], // Add CODEOWNERS |
| 37 | + ["git", ["commit", "-m", `"Update CODEOWNERS"`]], // Commit all changes |
| 38 | + ["git", ["remote", "add", "fork", remoteUrl]], // Add the remote fork |
| 39 | + ["git", ["push", "--set-upstream", "fork", branchName, "-f"]] // push the branch |
| 40 | + ]); |
| 41 | + |
| 42 | + gh.authenticate({ |
| 43 | + type: "token", |
| 44 | + token, |
| 45 | + }); |
| 46 | + return gh.pulls.create({ |
| 47 | + owner: "DefinitelyTyped", |
| 48 | + repo: "DefinitelyTyped", |
| 49 | + maintainer_can_modify: true, |
| 50 | + title: `🤖 CODEOWNERS has changed`, |
| 51 | + head: `DefinitelyTyped:${branchName}`, |
| 52 | + base: "master", |
| 53 | + body: |
| 54 | + `Please review the diff and merge if no changes are unexpected. |
| 55 | +
|
| 56 | +cc ${reviewers.map(r => "@" + r).join(" ")}`, |
| 57 | + }) |
| 58 | +}).then(r => { |
| 59 | + const num = r.data.number; |
| 60 | + console.log(`Pull request ${num} created.`); |
| 61 | + return gh.pulls.createReviewRequest({ |
| 62 | + owner: "DefinitelyTyped", |
| 63 | + repo: "DefinitelyTyped", |
| 64 | + number: num, |
| 65 | + reviewers, |
| 66 | + }); |
| 67 | +}).then(() => { |
| 68 | + console.log(`Reviewers requested, done.`); |
| 69 | +}).catch(e => { |
| 70 | + console.error(e); |
| 71 | + process.exit(1); |
| 72 | +}); |
| 73 | + |
| 74 | +/** @param {[string, string[]][]} tasks */ |
| 75 | +function runSequence(tasks) { |
| 76 | + for (const task of tasks) { |
| 77 | + console.log(`${task[0]} ${task[1].join(" ")}`); |
| 78 | + const result = cp.spawnSync(task[0], task[1], { timeout: 100000, shell: true, stdio: "inherit" }); |
| 79 | + if (result.status !== 0) throw new Error(`${task[0]} ${task[1].join(" ")} failed: ${result.stderr && result.stderr.toString()}`); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +/** @param {number} number */ |
| 84 | +function padNum(number) { |
| 85 | + const str = "" + number; |
| 86 | + return str.length >= 2 ? str : "0" + str; |
| 87 | +} |
| 88 | + |
| 89 | + |
| 90 | +const header = |
| 91 | +`# This file is generated. |
| 92 | +# Add yourself to the "Definitions by:" list instead. |
| 93 | +# See https://github.com/DefinitelyTyped/DefinitelyTyped#edit-an-existing-package`; |
| 94 | + |
| 95 | +/** |
| 96 | + * @param { { contributors: ReadonlyArray<{githubUsername?: string }>, subDirectoryPath: string} } pkg |
| 97 | + * @param {number} maxPathLen |
| 98 | + * @return {string | undefined} |
| 99 | + */ |
| 100 | +function getEntry(pkg, maxPathLen) { |
| 101 | + const users = mapDefined(pkg.contributors, c => c.githubUsername); |
| 102 | + if (!users.length) { |
| 103 | + return undefined; |
| 104 | + } |
| 105 | + |
| 106 | + const path = `${pkg.subDirectoryPath}/`.padEnd(maxPathLen); |
| 107 | + return `/types/${path} ${users.map(u => `@${u}`).join(" ")}`; |
| 108 | +} |
| 109 | + |
| 110 | +/** |
| 111 | + * @template T,U |
| 112 | + * @param {ReadonlyArray<T>} arr |
| 113 | + * @param {(t: T) => U | undefined} mapper |
| 114 | + * @return U[] |
| 115 | + */ |
| 116 | +function mapDefined(arr, mapper) { |
| 117 | + const out = []; |
| 118 | + for (const a of arr) { |
| 119 | + const res = mapper(a); |
| 120 | + if (res !== undefined) { |
| 121 | + out.push(res); |
| 122 | + } |
| 123 | + } |
| 124 | + return out; |
| 125 | +} |
| 126 | + |
0 commit comments