@@ -255,7 +255,7 @@ class GithubUtils {
255
255
}
256
256
257
257
/**
258
- * Generate the issue body for a StagingDeployCash.
258
+ * Generate the issue body and assignees for a StagingDeployCash.
259
259
*
260
260
* @param {String} tag
261
261
* @param {Array} PRList - The list of PR URLs which are included in this StagingDeployCash
@@ -268,7 +268,7 @@ class GithubUtils {
268
268
* @param {Boolean} [isGHStatusChecked]
269
269
* @returns {Promise}
270
270
*/
271
- static generateStagingDeployCashBody (
271
+ static generateStagingDeployCashBodyAndAssignees (
272
272
tag,
273
273
PRList,
274
274
verifiedPRList = [],
@@ -281,85 +281,89 @@ class GithubUtils {
281
281
) {
282
282
return this.fetchAllPullRequests(_.map(PRList, this.getPullRequestNumberFromURL))
283
283
.then((data) => {
284
- // The format of this map is following:
285
- // {
286
- // 'https://github.com/Expensify/App/pull/9641': [ 'PauloGasparSv', 'kidroca' ],
287
- // 'https://github.com/Expensify/App/pull/9642': [ 'mountiny', 'kidroca' ]
288
- // }
289
- const internalQAPRMap = _.reduce(
290
- _.filter(data, (pr) => !_.isEmpty(_.findWhere(pr.labels, {name: CONST.LABELS.INTERNAL_QA}))),
291
- (map, pr) => {
292
- // eslint-disable-next-line no-param-reassign
293
- map[pr.html_url] = _.compact(_.pluck(pr.assignees, 'login'));
294
- return map;
295
- },
296
- {},
297
- );
298
- console.log('Found the following Internal QA PRs:', internalQAPRMap);
299
-
300
- const noQAPRs = _.pluck(
301
- _.filter(data, (PR) => /\[No\s?QA]/i.test(PR.title)),
302
- 'html_url',
303
- );
304
- console.log('Found the following NO QA PRs:', noQAPRs);
305
- const verifiedOrNoQAPRs = _.union(verifiedPRList, noQAPRs);
306
-
307
- const sortedPRList = _.chain(PRList).difference(_.keys(internalQAPRMap)).unique().sortBy(GithubUtils.getPullRequestNumberFromURL).value();
308
- const sortedDeployBlockers = _.sortBy(_.unique(deployBlockers), GithubUtils.getIssueOrPullRequestNumberFromURL);
309
-
310
- // Tag version and comparison URL
311
- // eslint-disable-next-line max-len
312
- let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/Expensify/App/compare/production...staging\r\n`;
313
-
314
- // PR list
315
- if (!_.isEmpty(sortedPRList)) {
316
- issueBody += '\r\n**This release contains changes from the following pull requests:**\r\n';
317
- _.each(sortedPRList, (URL) => {
318
- issueBody += _.contains(verifiedOrNoQAPRs, URL) ? '- [x]' : '- [ ]';
319
- issueBody += ` ${URL}\r\n`;
320
- });
321
- issueBody += '\r\n\r\n';
322
- }
284
+ const internalQAPRs = _.filter(data, (pr) => !_.isEmpty(_.findWhere(pr.labels, {name: CONST.LABELS.INTERNAL_QA})));
285
+ return Promise.all(_.map(internalQAPRs, (pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({url: pr.html_url, mergerLogin})))).then((results) => {
286
+ // The format of this map is following:
287
+ // {
288
+ // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv',
289
+ // 'https://github.com/Expensify/App/pull/9642': 'mountiny'
290
+ // }
291
+ const internalQAPRMap = _.reduce(
292
+ results,
293
+ (acc, {url, mergerLogin}) => {
294
+ acc[url] = mergerLogin;
295
+ return acc;
296
+ },
297
+ {},
298
+ );
299
+ console.log('Found the following Internal QA PRs:', internalQAPRMap);
300
+
301
+ const noQAPRs = _.pluck(
302
+ _.filter(data, (PR) => /\[No\s?QA]/i.test(PR.title)),
303
+ 'html_url',
304
+ );
305
+ console.log('Found the following NO QA PRs:', noQAPRs);
306
+ const verifiedOrNoQAPRs = _.union(verifiedPRList, noQAPRs);
307
+
308
+ const sortedPRList = _.chain(PRList).difference(_.keys(internalQAPRMap)).unique().sortBy(GithubUtils.getPullRequestNumberFromURL).value();
309
+ const sortedDeployBlockers = _.sortBy(_.unique(deployBlockers), GithubUtils.getIssueOrPullRequestNumberFromURL);
310
+
311
+ // Tag version and comparison URL
312
+ // eslint-disable-next-line max-len
313
+ let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/Expensify/App/compare/production...staging\r\n`;
314
+
315
+ // PR list
316
+ if (!_.isEmpty(sortedPRList)) {
317
+ issueBody += '\r\n**This release contains changes from the following pull requests:**\r\n';
318
+ _.each(sortedPRList, (URL) => {
319
+ issueBody += _.contains(verifiedOrNoQAPRs, URL) ? '- [x]' : '- [ ]';
320
+ issueBody += ` ${URL}\r\n`;
321
+ });
322
+ issueBody += '\r\n\r\n';
323
+ }
323
324
324
- // Internal QA PR list
325
- if (!_.isEmpty(internalQAPRMap)) {
326
- console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs);
327
- issueBody += '**Internal QA:**\r\n';
328
- _.each(internalQAPRMap, (assignees , URL) => {
329
- const assigneeMentions = _.reduce(assignees, (memo, assignee) => `${memo} @${assignee}`, '') ;
330
- issueBody += `${_.contains(resolvedInternalQAPRs, URL) ? '- [x]' : '- [ ]'} `;
331
- issueBody += `${URL}`;
332
- issueBody += ` -${assigneeMentions }`;
333
- issueBody += '\r\n';
334
- });
335
- issueBody += '\r\n\r\n';
336
- }
325
+ // Internal QA PR list
326
+ if (!_.isEmpty(internalQAPRMap)) {
327
+ console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs);
328
+ issueBody += '**Internal QA:**\r\n';
329
+ _.each(internalQAPRMap, (merger , URL) => {
330
+ const mergerMention = ` @${merger}` ;
331
+ issueBody += `${_.contains(resolvedInternalQAPRs, URL) ? '- [x]' : '- [ ]'} `;
332
+ issueBody += `${URL}`;
333
+ issueBody += ` - ${mergerMention }`;
334
+ issueBody += '\r\n';
335
+ });
336
+ issueBody += '\r\n\r\n';
337
+ }
337
338
338
- // Deploy blockers
339
- if (!_.isEmpty(deployBlockers)) {
340
- issueBody += '**Deploy Blockers:**\r\n';
341
- _.each(sortedDeployBlockers, (URL) => {
342
- issueBody += _.contains(resolvedDeployBlockers, URL) ? '- [x] ' : '- [ ] ';
343
- issueBody += URL;
344
- issueBody += '\r\n';
345
- });
346
- issueBody += '\r\n\r\n';
347
- }
339
+ // Deploy blockers
340
+ if (!_.isEmpty(deployBlockers)) {
341
+ issueBody += '**Deploy Blockers:**\r\n';
342
+ _.each(sortedDeployBlockers, (URL) => {
343
+ issueBody += _.contains(resolvedDeployBlockers, URL) ? '- [x] ' : '- [ ] ';
344
+ issueBody += URL;
345
+ issueBody += '\r\n';
346
+ });
347
+ issueBody += '\r\n\r\n';
348
+ }
348
349
349
- issueBody += '**Deployer verifications:**';
350
- // eslint-disable-next-line max-len
351
- issueBody += `\r\n- [${
352
- isTimingDashboardChecked ? 'x' : ' '
353
- }] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
354
- // eslint-disable-next-line max-len
355
- issueBody += `\r\n- [${
356
- isFirebaseChecked ? 'x' : ' '
357
- }] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`;
358
- // eslint-disable-next-line max-len
359
- issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`;
360
-
361
- issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
362
- return issueBody;
350
+ issueBody += '**Deployer verifications:**';
351
+ // eslint-disable-next-line max-len
352
+ issueBody += `\r\n- [${
353
+ isTimingDashboardChecked ? 'x' : ' '
354
+ }] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`;
355
+ // eslint-disable-next-line max-len
356
+ issueBody += `\r\n- [${
357
+ isFirebaseChecked ? 'x' : ' '
358
+ }] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`;
359
+ // eslint-disable-next-line max-len
360
+ issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`;
361
+
362
+ issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
363
+ const issueAssignees = _.uniq(_.values(internalQAPRMap));
364
+ const issue = {issueBody, issueAssignees};
365
+ return issue;
366
+ });
363
367
})
364
368
.catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err));
365
369
}
@@ -393,6 +397,20 @@ class GithubUtils {
393
397
.catch((err) => console.error('Failed to get PR list', err));
394
398
}
395
399
400
+ /**
401
+ * @param {Number} pullRequestNumber
402
+ * @returns {Promise}
403
+ */
404
+ static getPullRequestMergerLogin(pullRequestNumber) {
405
+ return this.octokit.pulls
406
+ .get({
407
+ owner: CONST.GITHUB_OWNER,
408
+ repo: CONST.APP_REPO,
409
+ pull_number: pullRequestNumber,
410
+ })
411
+ .then(({data: pullRequest}) => pullRequest.merged_by.login);
412
+ }
413
+
396
414
/**
397
415
* @param {Number} pullRequestNumber
398
416
* @returns {Promise}
0 commit comments