Skip to content

Commit 6b95e7c

Browse files
authored
Refactor build queueing, add new perf pipeline under new name (#29)
1 parent 3dd093a commit 6b95e7c

File tree

1 file changed

+131
-39
lines changed

1 file changed

+131
-39
lines changed

GithubCommentReader/index.js

Lines changed: 131 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function getGHClient() {
1919
}
2020
}
2121

22+
const typeScriptProjectId = "cf7ac146-d525-443c-b23c-0d58337efebc";
23+
2224
function getVSTSTypeScriptClient() {
2325
if (clients.vstsTypescript) {
2426
return clients.vstsTypescript;
@@ -31,33 +33,16 @@ function getVSTSTypeScriptClient() {
3133
}
3234
}
3335

34-
/**
35-
* @typedef {{
36-
* definition: {
37-
* id: number;
38-
* };
39-
* queue: {
40-
* id: number;
41-
* };
42-
* project: {
43-
* id: string;
44-
* };
45-
* sourceBranch: string;
46-
* sourceVersion: string;
47-
* parameters: string;
48-
* }} BuildVars
49-
*/
50-
5136
/**
5237
* Authenticate with github and vsts, make a comment saying what's being done, then schedule the build
5338
* and update the comment with the build log URL.
5439
* @param {*} request The request object
5540
* @param {string} suiteName The frindly name to call the suite in the associated comment
5641
* @param {number} definitionId The VSTS id of the build definition to trigger
5742
* @param {(s: string) => void} log
58-
* @param {(x: BuildVars) => (Promise<BuildVars> | BuildVars)} buildTriggerAugmentor maps the intial build request into an enhanced one
43+
* @param {(pr: Client.Octokit.PullsGetResponse, commentId: number) => Promise<string>} buildTrigger
5944
*/
60-
async function makeNewBuildWithComments(request, suiteName, definitionId, log, buildTriggerAugmentor = p => p) {
45+
async function commentAndTriggerBuild(request, suiteName, definitionId, log, buildTrigger) {
6146
log(`New build for ${suiteName} (${definitionId}) on ${request.issue.number}`)
6247
const cli = getGHClient();
6348
log("Got github client")
@@ -73,41 +58,124 @@ async function makeNewBuildWithComments(request, suiteName, definitionId, log, b
7358
});
7459
const commentId = result.data.id;
7560
log(`Created new "started running" comment ${commentId}`)
76-
const buildQueue = await triggerBuild(request, pr, definitionId, log, p => buildTriggerAugmentor({ ...p, parameters: JSON.stringify({ ...JSON.parse(p.parameters), status_comment: commentId }) }));
61+
const buildUrl = await buildTrigger(pr, commentId);
7762
log(`Build done queueing`)
7863
await cli.issues.updateComment({
7964
owner: "microsoft",
8065
repo: "TypeScript",
8166
comment_id: commentId,
82-
body: `Heya @${requestingUser}, I've started to run the ${suiteName} on this PR at ${refSha}. You can monitor the build [here](${buildQueue._links.web.href}).`
67+
body: `Heya @${requestingUser}, I've started to run the ${suiteName} on this PR at ${refSha}. You can monitor the build [here](${buildUrl}).`
8368
});
8469
log(`Updated to "build is queued" comment ${commentId}`)
8570
}
8671

8772
/**
88-
* Authenticate with vsts and schedule the build
73+
* @typedef {{
74+
* definition: {
75+
* id: number;
76+
* };
77+
* queue: {
78+
* id: number;
79+
* };
80+
* project: {
81+
* id: string;
82+
* };
83+
* sourceBranch: string;
84+
* sourceVersion: string;
85+
* parameters: string;
86+
* }} BuildVars
87+
*/
88+
89+
/**
90+
* Authenticate with github and vsts, make a comment saying what's being done, then schedule the build
91+
* and update the comment with the build log URL.
8992
* @param {*} request The request object
90-
* @param {*} pr The github PR data object
93+
* @param {string} suiteName The frindly name to call the suite in the associated comment
9194
* @param {number} definitionId The VSTS id of the build definition to trigger
9295
* @param {(s: string) => void} log
9396
* @param {(x: BuildVars) => (Promise<BuildVars> | BuildVars)} buildTriggerAugmentor maps the intial build request into an enhanced one
9497
*/
95-
async function triggerBuild(request, pr, definitionId, log, buildTriggerAugmentor = p => p) {
96-
log(`Trigger build ${definitionId} on ${request.issue.number}`)
97-
const build = await getVSTSTypeScriptClient().getBuildApi();
98-
log("Got VSTS Client's Build API")
99-
const requestingUser = request.comment.user.login;
100-
let buildParams = /** @type BuildVars & { templateParameters: Record<string, string> } */ (await buildTriggerAugmentor({
101-
definition: { id: definitionId },
102-
queue: { id: 26 },
103-
project: { id: "cf7ac146-d525-443c-b23c-0d58337efebc" },
104-
sourceBranch: `refs/pull/${pr.number}/merge`, // Undocumented, but used by the official frontend
105-
sourceVersion: ``, // Also undocumented
106-
parameters: JSON.stringify({ source_issue: pr.number, requesting_user: requestingUser }), // This API is real bad
107-
}));
108-
buildParams.templateParameters = JSON.parse(buildParams.parameters);
109-
log(`Final template parameters after augmentation: ${JSON.stringify(buildParams.templateParameters)}`)
110-
return await build.queueBuild(buildParams, "TypeScript");
98+
async function makeNewBuildWithComments(request, suiteName, definitionId, log, buildTriggerAugmentor = p => p) {
99+
await commentAndTriggerBuild(request, suiteName, definitionId, log, async (pr, commentId) => {
100+
log(`Trigger build ${definitionId} on ${request.issue.number}`)
101+
const build = await getVSTSTypeScriptClient().getBuildApi();
102+
log("Got VSTS Client's Build API")
103+
const requestingUser = request.comment.user.login;
104+
let buildParams = /** @type BuildVars & { templateParameters: Record<string, string> } */ (await buildTriggerAugmentor({
105+
definition: { id: definitionId },
106+
queue: { id: 26 },
107+
project: { id: typeScriptProjectId },
108+
sourceBranch: `refs/pull/${pr.number}/merge`, // Undocumented, but used by the official frontend
109+
sourceVersion: ``, // Also undocumented
110+
parameters: JSON.stringify({ source_issue: pr.number, requesting_user: requestingUser, status_comment: commentId }), // This API is real bad
111+
}));
112+
buildParams.templateParameters = JSON.parse(buildParams.parameters);
113+
log(`Final template parameters after augmentation: ${JSON.stringify(buildParams.templateParameters)}`)
114+
const response = await build.queueBuild(buildParams, "TypeScript");
115+
return response._links.web.href;
116+
})
117+
}
118+
119+
120+
/**
121+
* @typedef {{
122+
* resources?: {
123+
* repositories?: Record<string, { refName?: string; version?: string } | undefined>;
124+
* };
125+
* variables?: Record<string, { isSecret?: boolean; value?: string; } | undefined>;
126+
* templateParameters?: Record<string, string | number | boolean | undefined>;
127+
* queue?: undefined;
128+
* sourceBranch?: undefined;
129+
* sourceVersion?: undefined;
130+
* parameters?: undefined;
131+
* }} PipelineRunArgs
132+
*/
133+
134+
/**
135+
* Authenticate with github and vsts, make a comment saying what's being done, then schedule the build
136+
* and update the comment with the build log URL.
137+
* @param {*} request The request object
138+
* @param {string} suiteName The frindly name to call the suite in the associated comment
139+
* @param {number} definitionId The VSTS id of the build definition to trigger
140+
* @param {(s: string) => void} log
141+
* @param {(x: PipelineRunArgs) => (Promise<PipelineRunArgs> | PipelineRunArgs)} buildTriggerAugmentor maps the intial build request into an enhanced one
142+
*/
143+
async function makeNewPipelineRunWithComments(request, suiteName, definitionId, log, buildTriggerAugmentor = p => p) {
144+
await commentAndTriggerBuild(request, suiteName, definitionId, log, async (pr, commentId) => {
145+
log(`Trigger pipeline ${definitionId} on ${request.issue.number}`)
146+
const build = await getVSTSTypeScriptClient().getBuildApi();
147+
log("Got VSTS Client's Build API")
148+
149+
// The new pipelines API is not yet supported by the node client, so we have to do this manually.
150+
// The request was reverse engineered from the HTTP requests made by the azure devops UI, the node client, and the Go client (which has implemented this).
151+
// https://github.com/microsoft/azure-devops-go-api/blob/8dbf8bfd3346f337d914961fab01df812985dcb8/azuredevops/v7/pipelines/client.go#L446
152+
const verData = await build.vsoClient.getVersioningData("7.1-preview.1", "pipelines", "7859261e-d2e9-4a68-b820-a5d84cc5bb3d", { project: typeScriptProjectId, pipelineId: definitionId });
153+
const url = verData.requestUrl;
154+
const options = build.createRequestOptions('application/json', verData.apiVersion);
155+
assert(url);
156+
157+
const requestingUser = request.comment.user.login;
158+
/** @type {PipelineRunArgs} */
159+
let args = {
160+
resources: {
161+
repositories: {
162+
self: {
163+
refName: `refs/pull/${pr.number}/merge`,
164+
}
165+
}
166+
},
167+
templateParameters: {
168+
source_issue: pr.number,
169+
requesting_user: requestingUser,
170+
status_comment: commentId,
171+
}
172+
}
173+
args = await buildTriggerAugmentor(args);
174+
175+
log(`Final template parameters after augmentation: ${JSON.stringify(args)}`)
176+
const response = await build.rest.create(url, args, options);
177+
return response.result._links.web.href;
178+
})
111179
}
112180

113181
/**
@@ -238,6 +306,30 @@ const commands = (/** @type {Map<RegExp, CommentAction>} */(new Map()))
238306
.set(/pack this/, action(async (request, log) => await makeNewBuildWithComments(request, "tarball bundle task", 19, log)))
239307
.set(/perf test(?: this)?(?! this)(?! faster)/, action(async (request, log) => await makeNewBuildWithComments(request, "perf test suite", 22, log, p => ({...p, queue: { id: 22 }}))))
240308
.set(/perf test(?: this)? faster/, action(async (request, log) => await makeNewBuildWithComments(request, "abridged perf test suite", 45, log, p => ({...p, queue: { id: 22 }}))))
309+
.set(/new perf test(?: this)?(?: (\S+)?)?/, action(async (request, log, match) => {
310+
let preset = match[1] || "regular";
311+
if (preset === "faster") preset = "tsc-only";
312+
313+
await makeNewPipelineRunWithComments(request, `${preset} perf test suite`, 69, log, p => {
314+
// makeNewPipelineRunWithComments assumes that the pipeline is defined on TypeScript,
315+
// but this pipeline is defined on typescript-benchmarking, so we move the self reference
316+
// over to TypeScript (the name known to the benchmark pipeline).
317+
const self = p.resources?.repositories?.self;
318+
assert(self);
319+
return {
320+
...p,
321+
resources: {
322+
repositories: {
323+
TypeScript: self,
324+
}
325+
},
326+
templateParameters: {
327+
...p.templateParameters,
328+
tsperf_preset: preset,
329+
}
330+
}
331+
});
332+
}))
241333
.set(/run dt(?! slower)/, action(async (request, log) => await makeNewBuildWithComments(request, "parallelized Definitely Typed test suite", 23, log, async p => ({
242334
...p,
243335
parameters: JSON.stringify({

0 commit comments

Comments
 (0)