forked from microsoft/typescript-error-deltas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgitUtils.ts
183 lines (150 loc) · 5.31 KB
/
gitUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import octokit = require("@octokit/rest");
import utils = require("./packageUtils");
import git = require("simple-git/promise");
import fs = require("fs");
import path = require("path");
import cp = require("child_process");
export interface Repo {
name: string;
url?: string;
owner?: string;
types?: string[];
branch?: string;
}
const repoProperties = {
owner: "microsoft",
repo: "typescript",
};
export async function getPopularTypeScriptRepos(count = 100, cachePath?: string): Promise<readonly Repo[]> {
const cacheEncoding = { encoding: "utf-8" } as const;
if (cachePath && await utils.exists(cachePath)) {
const contents = await fs.promises.readFile(cachePath, cacheEncoding);
const cache: Repo[] = JSON.parse(contents);
if (cache.length >= count) {
return cache.slice(0, count);
}
}
const kit = new octokit.Octokit();
const perPage = Math.min(100, count);
let repos: Repo[] = [];
for (let page = 1; repos.length < count; page++) {
const response = await kit.search.repos({
q: "language:TypeScript+stars:>100 archived:no",
sort: "stars",
order: "desc",
per_page: perPage,
page,
});
if (response.status !== 200) throw response;
for (const repo of response.data.items) {
if (repo.full_name !== "microsoft/TypeScript" && repo.full_name !== "DefinitelyTyped/DefinitelyTyped") {
repos.push({ url: repo.html_url, name: repo.name, owner: repo.owner.login });
}
if (repos.length >= count) {
break;
}
}
if (!response.headers.link || !response.headers.link.includes('rel="next"')) break;
}
if (cachePath) {
await fs.promises.writeFile(cachePath, JSON.stringify(repos), cacheEncoding);
}
return repos;
}
export async function cloneRepoIfNecessary(parentDir: string, repo: Repo): Promise<void> {
if (!repo.url) {
throw new Error("Repo url cannot be `undefined`");
}
if (!await utils.exists(path.join(parentDir, repo.name))) {
console.log(`Cloning ${repo.url} into ${repo.name}`);
let options = ["--recurse-submodules", "--depth=1"];
if (repo.branch) {
options.push(`--branch=${repo.branch}`);
}
await git(parentDir).clone(repo.url, repo.name, options);
}
}
type Result = {
body: string,
owner: string,
repo: string,
}
export type GitResult = Result & { kind: 'git', title: string }
export type UserResult = Result & { kind: 'user', issue_number: number }
export async function createIssue(postResult: boolean, title: string, body: string, sawNewErrors: boolean): Promise<GitResult | undefined> {
const issue = {
...repoProperties,
title,
body,
};
if (!postResult) {
console.log("Issue not posted: ");
console.log(JSON.stringify(issue));
return { kind: 'git', ...issue };
}
console.log("Creating a summary issue");
const kit = new octokit.Octokit({
auth: process.env.GITHUB_PAT,
});
const created = await kit.issues.create(issue);
const issueNumber = created.data.number;
console.log(`Created issue #${issueNumber}: ${created.data.html_url}`);
if (!sawNewErrors) {
await kit.issues.update({
...repoProperties,
issue_number: issueNumber,
state: "closed",
});
}
}
export async function createComment(sourceIssue: number, statusComment: number, postResult: boolean, body: string): Promise<UserResult | undefined> {
const newComment = {
...repoProperties,
issue_number: sourceIssue,
body,
};
if (!postResult) {
console.log("Comment not posted: ");
console.log(JSON.stringify(newComment));
return { kind: 'user', ...newComment };
}
console.log("Creating a github comment");
const kit = new octokit.Octokit({
auth: process.env.GITHUB_PAT,
});
const data = await kit.issues.createComment(newComment);
const newCommentUrl = data.data.html_url;
console.log(`Created comment #${data.data.id}: ${newCommentUrl}`);
// Update typescript-bot comment
const comment = await kit.issues.getComment({
...repoProperties,
comment_id: statusComment
});
const newBody = `${comment.data.body}\n\nUpdate: [The results are in!](${newCommentUrl})`;
await kit.issues.updateComment({
...repoProperties,
comment_id: statusComment,
body: newBody
});
}
export async function checkout(cwd: string, branch: string) {
await execAsync(cwd, `git fetch origin ${branch}:${branch} --recurse-submodules --depth=1`);
await execAsync(cwd, `git checkout ${branch}`);
}
async function execAsync(cwd: string, command: string): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`${cwd}> ${command}`);
cp.exec(command, { cwd }, (err, stdout, stderr) => {
if (stdout?.length) {
console.log(stdout);
}
if (stderr?.length) {
console.log(stderr); // To stdout to maintain order
}
if (err) {
return reject(err);
}
return resolve(stdout);
});
});
}