Skip to content

Commit a33948f

Browse files
authored
Merge pull request #3641 from Expensify/update-staging-from-main
2 parents 4166381 + 298804c commit a33948f

File tree

54 files changed

+1092
-1149
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1092
-1149
lines changed

.github/actions/checkDeployBlockers/index.js

Lines changed: 4 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const ISSUE_OR_PULL_REQUEST_REGEX = new RegExp(`${GITHUB_BASE_URL_REGEX.source}/
103103

104104
const APPLAUSE_BOT = 'applausebot';
105105
const STAGING_DEPLOY_CASH_LABEL = 'StagingDeployCash';
106+
const DEPLOY_BLOCKER_CASH_LABEL = 'DeployBlockerCash';
106107

107108
class GithubUtils {
108109
/**
@@ -185,6 +186,7 @@ class GithubUtils {
185186
return {
186187
title: issue.title,
187188
url: issue.url,
189+
number: this.getIssueOrPullRequestNumberFromURL(issue.url),
188190
labels: issue.labels,
189191
PRList: this.getStagingDeployCashPRList(issue),
190192
deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
@@ -269,87 +271,9 @@ class GithubUtils {
269271
);
270272
}
271273

272-
/**
273-
* Creates a new StagingDeployCash issue.
274-
*
275-
* @param {String} title
276-
* @param {String} tag
277-
* @param {Array} PRList
278-
* @returns {Promise}
279-
*/
280-
static createNewStagingDeployCash(title, tag, PRList) {
281-
return this.generateStagingDeployCashBody(tag, PRList)
282-
.then(body => this.octokit.issues.create({
283-
owner: GITHUB_OWNER,
284-
repo: EXPENSIFY_CASH_REPO,
285-
labels: [STAGING_DEPLOY_CASH_LABEL],
286-
assignees: [APPLAUSE_BOT],
287-
title,
288-
body,
289-
}));
290-
}
291-
292-
/**
293-
* Updates the existing open StagingDeployCash issue.
294-
*
295-
* @param {String} [newTag]
296-
* @param {Array} newPRs
297-
* @param {Array} newDeployBlockers
298-
* @returns {Promise}
299-
* @throws {Error} If the StagingDeployCash could not be found or updated.
300-
*/
301-
static updateStagingDeployCash(newTag = '', newPRs, newDeployBlockers) {
302-
let issueNumber;
303-
return this.getStagingDeployCash()
304-
.then(({
305-
url,
306-
tag: oldTag,
307-
PRList: oldPRs,
308-
deployBlockers: oldDeployBlockers,
309-
}) => {
310-
issueNumber = GithubUtils.getIssueNumberFromURL(url);
311-
312-
// If we aren't sent a tag, then use the existing tag
313-
const tag = _.isEmpty(newTag) ? oldTag : newTag;
314-
315-
const PRList = _.sortBy(
316-
_.union(oldPRs, _.map(newPRs, URL => ({
317-
url: URL,
318-
number: GithubUtils.getPullRequestNumberFromURL(URL),
319-
isVerified: false,
320-
}))),
321-
'number',
322-
);
323-
const deployBlockers = _.sortBy(
324-
_.union(oldDeployBlockers, _.map(newDeployBlockers, URL => ({
325-
url: URL,
326-
number: GithubUtils.getIssueOrPullRequestNumberFromURL(URL),
327-
isResolved: false,
328-
}))),
329-
'number',
330-
);
331-
332-
return this.generateStagingDeployCashBody(
333-
tag,
334-
_.pluck(PRList, 'url'),
335-
_.pluck(_.where(PRList, {isVerified: true}), 'url'),
336-
_.pluck(deployBlockers, 'url'),
337-
_.pluck(_.where(deployBlockers, {isResolved: true}), 'url'),
338-
);
339-
})
340-
.then(updatedBody => this.octokit.issues.update({
341-
owner: GITHUB_OWNER,
342-
repo: EXPENSIFY_CASH_REPO,
343-
issue_number: issueNumber,
344-
body: updatedBody,
345-
}));
346-
}
347-
348274
/**
349275
* Generate the issue body for a StagingDeployCash.
350276
*
351-
* @private
352-
*
353277
* @param {String} tag
354278
* @param {Array} PRList - The list of PR URLs which are included in this StagingDeployCash
355279
* @param {Array} [verifiedPRList] - The list of PR URLs which have passed QA.
@@ -533,6 +457,8 @@ module.exports = GithubUtils;
533457
module.exports.GITHUB_OWNER = GITHUB_OWNER;
534458
module.exports.EXPENSIFY_CASH_REPO = EXPENSIFY_CASH_REPO;
535459
module.exports.STAGING_DEPLOY_CASH_LABEL = STAGING_DEPLOY_CASH_LABEL;
460+
module.exports.DEPLOY_BLOCKER_CASH_LABEL = DEPLOY_BLOCKER_CASH_LABEL;
461+
module.exports.APPLAUSE_BOT = APPLAUSE_BOT;
536462

537463

538464
/***/ }),

.github/actions/createOrUpdateStagingDeploy/action.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ inputs:
77
NPM_VERSION:
88
description: The new NPM version of the StagingDeployCash issue.
99
required: false
10-
NEW_PULL_REQUESTS:
11-
description: A comma-separated list of pull request URLs to add to the open StagingDeployCash issue.
12-
required: false
13-
NEW_DEPLOY_BLOCKERS:
14-
description: A comma-separated list of deploy blockers to add to the open StagingDeployCash issue.
15-
required: false
1610
runs:
1711
using: 'node12'
1812
main: './index.js'

.github/actions/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js

Lines changed: 139 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,145 @@ const moment = require('moment');
44
const GithubUtils = require('../../libs/GithubUtils');
55
const GitUtils = require('../../libs/GitUtils');
66

7-
const newVersion = core.getInput('NPM_VERSION');
8-
9-
10-
GithubUtils.getStagingDeployCash()
11-
.then(() => GithubUtils.updateStagingDeployCash(
12-
newVersion,
13-
_.filter(
14-
_.map(core.getInput('NEW_PULL_REQUESTS').split(','), PR => PR.trim()),
15-
PR => !_.isEmpty(PR),
16-
),
17-
_.filter(
18-
_.map(core.getInput('NEW_DEPLOY_BLOCKERS').split(','), deployBlocker => deployBlocker.trim()),
19-
PR => !_.isEmpty(PR),
20-
),
21-
))
22-
.then(({data}) => {
23-
console.log('Successfully updated StagingDeployCash! 🎉', data.html_url);
24-
process.exit(0);
25-
})
26-
.catch((err) => {
27-
// Unable to find the open StagingDeployCash
28-
if (err && err.code === 404) {
29-
console.log('No open StagingDeployCash found, creating a new one.');
30-
31-
// Fetch all the StagingDeployCash issues
32-
return GithubUtils.octokit.issues.listForRepo({
33-
log: console,
7+
const run = function () {
8+
const newVersion = core.getInput('NPM_VERSION');
9+
10+
let shouldCreateNewStagingDeployCash = false;
11+
let currentStagingDeployCashIssueNumber = null;
12+
13+
// Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers
14+
return Promise.all([
15+
GithubUtils.octokit.issues.listForRepo({
16+
log: console,
17+
owner: GithubUtils.GITHUB_OWNER,
18+
repo: GithubUtils.EXPENSIFY_CASH_REPO,
19+
labels: GithubUtils.STAGING_DEPLOY_CASH_LABEL,
20+
}),
21+
GithubUtils.octokit.issues.listForRepo({
22+
log: console,
23+
owner: GithubUtils.GITHUB_OWNER,
24+
repo: GithubUtils.EXPENSIFY_CASH_REPO,
25+
labels: GithubUtils.DEPLOY_BLOCKER_CASH_LABEL,
26+
state: 'open',
27+
}),
28+
])
29+
.then((results) => {
30+
const [stagingDeployResponse, deployBlockerResponse] = results;
31+
if (!stagingDeployResponse || !stagingDeployResponse.data || _.isEmpty(stagingDeployResponse.data)) {
32+
console.error('Failed fetching StagingDeployCash issues from Github!', stagingDeployResponse);
33+
throw new Error('Failed fetching StagingDeployCash issues from Github');
34+
}
35+
36+
if (!deployBlockerResponse || !deployBlockerResponse.data) {
37+
console.log('Failed fetching DeployBlockerCash issues from Github, continuing...');
38+
}
39+
40+
// Look at the state of the most recent StagingDeployCash,
41+
// if it is open then we'll update the existing one, otherwise, we'll create a new one.
42+
shouldCreateNewStagingDeployCash = Boolean(stagingDeployResponse.data[0].state !== 'open');
43+
44+
// Parse the data from the previous StagingDeployCash
45+
// (newest if there are none open, otherwise second-newest)
46+
const previousStagingDeployCashData = shouldCreateNewStagingDeployCash
47+
? GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0])
48+
: GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[1]);
49+
50+
console.log('Found tag of previous StagingDeployCash:', previousStagingDeployCashData.tag);
51+
52+
// Find the list of PRs merged between the last StagingDeployCash and the new version
53+
const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newVersion);
54+
55+
if (shouldCreateNewStagingDeployCash) {
56+
// We're in the create flow, not update
57+
// TODO: if there are open DeployBlockers and we are opening a new checklist,
58+
// then we should close / remove the DeployBlockerCash label from those
59+
return GithubUtils.generateStagingDeployCashBody(newVersion, mergedPRs);
60+
}
61+
62+
// There is an open StagingDeployCash, so we'll be updating it, not creating a new one
63+
const currentStagingDeployCashData = GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]);
64+
currentStagingDeployCashIssueNumber = currentStagingDeployCashData.number;
65+
66+
const newDeployBlockers = _.map(deployBlockerResponse.data, ({url}) => ({
67+
url,
68+
number: GithubUtils.getIssueOrPullRequestNumberFromURL(url),
69+
isResolved: false,
70+
}));
71+
72+
// If we aren't sent a tag, then use the existing tag
73+
const tag = newVersion || currentStagingDeployCashData.tag;
74+
75+
// Generate the PR list, preserving the previous state of `isVerified` for existing PRs
76+
const PRList = _.sortBy(
77+
_.unique(
78+
_.union(currentStagingDeployCashData.PRList, _.map(mergedPRs, url => ({
79+
url,
80+
number: GithubUtils.getPullRequestNumberFromURL(url),
81+
82+
// Since this is the second argument to _.union,
83+
// it will appear later in the array than any duplicate.
84+
// Since it is later in the array, it will be truncated by _.unique,
85+
// and the original value of isVerified will be preserved.
86+
isVerified: false,
87+
}))),
88+
false,
89+
item => item.number,
90+
),
91+
'number',
92+
);
93+
94+
// Generate the deploy blocker list, preserving the previous state of `isResolved`
95+
const deployBlockers = _.sortBy(
96+
_.unique(
97+
_.union(currentStagingDeployCashData.deployBlockers, newDeployBlockers),
98+
false,
99+
item => item.number,
100+
),
101+
'number',
102+
);
103+
104+
return GithubUtils.generateStagingDeployCashBody(
105+
tag,
106+
_.pluck(PRList, 'url'),
107+
_.pluck(_.where(PRList, {isVerified: true}), 'url'),
108+
_.pluck(deployBlockers, 'url'),
109+
_.pluck(_.where(deployBlockers, {isResolved: true}), 'url'),
110+
);
111+
})
112+
.then((body) => {
113+
const defaultPayload = {
34114
owner: GithubUtils.GITHUB_OWNER,
35115
repo: GithubUtils.EXPENSIFY_CASH_REPO,
36-
labels: GithubUtils.STAGING_DEPLOY_CASH_LABEL,
37-
state: 'closed',
116+
body,
117+
};
118+
119+
if (shouldCreateNewStagingDeployCash) {
120+
return GithubUtils.octokit.issues.create({
121+
...defaultPayload,
122+
title: `Deploy Checklist: Expensify.cash ${moment().format('YYYY-MM-DD')}`,
123+
labels: [GithubUtils.STAGING_DEPLOY_CASH_LABEL],
124+
assignees: [GithubUtils.APPLAUSE_BOT],
125+
});
126+
}
127+
128+
return GithubUtils.octokit.issues.update({
129+
...defaultPayload,
130+
issue_number: currentStagingDeployCashIssueNumber,
38131
});
39-
}
40-
41-
// Unexpected error!
42-
console.error('Unexpected error occurred finding the StagingDeployCash!'
43-
+ ' There may have been more than one open StagingDeployCash found,'
44-
+ ' or there was some other problem with the Github API request.', err);
45-
core.setFailed(err);
46-
})
47-
.then((githubResponse) => {
48-
if (!githubResponse || !githubResponse.data || _.isEmpty(githubResponse.data)) {
49-
console.error('Failed fetching data from Github!', githubResponse);
50-
throw new Error('Failed fetching data from Github');
51-
}
52-
53-
// Parse the tag from the most recent StagingDeployCash
54-
const lastTag = GithubUtils.getStagingDeployCashData(githubResponse.data[0]).tag;
55-
console.log('Found tag of previous StagingDeployCash:', lastTag);
56-
57-
// Find the list of PRs merged between the last StagingDeployCash and the new version
58-
return GitUtils.getPullRequestsMergedBetween(lastTag, newVersion);
59-
})
60-
.then(PRNumbers => GithubUtils.createNewStagingDeployCash(
61-
`Deploy Checklist: Expensify.cash ${moment().format('YYYY-MM-DD')}`,
62-
newVersion,
63-
_.map(PRNumbers, GithubUtils.getPullRequestURLFromNumber),
64-
))
65-
.then(({data}) => console.log('Successfully created new StagingDeployCash! 🎉', data.html_url))
66-
.catch((err) => {
67-
console.error('An error occurred!', err);
68-
core.setFailed(err);
69-
});
132+
})
133+
.then(({data}) => {
134+
// eslint-disable-next-line max-len
135+
console.log(`Successfully ${shouldCreateNewStagingDeployCash ? 'created new' : 'updated'} StagingDeployCash! 🎉 ${data.html_url}`);
136+
return data;
137+
})
138+
.catch((err) => {
139+
console.error('An unknown error occurred!', err);
140+
core.setFailed(err);
141+
});
142+
};
143+
144+
if (require.main === module) {
145+
run();
146+
}
147+
148+
module.exports = run;

0 commit comments

Comments
 (0)