@@ -15685,9 +15685,7 @@ const utils_1 = __nccwpck_require__(3030);
1568515685const plugin_paginate_rest_1 = __nccwpck_require__(4193);
1568615686const plugin_throttling_1 = __nccwpck_require__(9968);
1568715687const request_error_1 = __nccwpck_require__(537);
15688- const arrayDifference_1 = __importDefault(__nccwpck_require__(7532));
1568915688const CONST_1 = __importDefault(__nccwpck_require__(9873));
15690- const isEmptyObject_1 = __nccwpck_require__(6497);
1569115689class GithubUtils {
1569215690 static internalOctokit;
1569315691 /**
@@ -15766,219 +15764,6 @@ class GithubUtils {
1576615764 // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
1576715765 return this.internalOctokit.paginate;
1576815766 }
15769- /**
15770- * Finds one open `StagingDeployCash` issue via GitHub octokit library.
15771- */
15772- static getStagingDeployCash() {
15773- return this.octokit.issues
15774- .listForRepo({
15775- owner: CONST_1.default.GITHUB_OWNER,
15776- repo: CONST_1.default.APP_REPO,
15777- labels: CONST_1.default.LABELS.STAGING_DEPLOY,
15778- state: 'open',
15779- })
15780- .then(({ data }) => {
15781- if (!data.length) {
15782- throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`);
15783- }
15784- if (data.length > 1) {
15785- throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`);
15786- }
15787- const issue = data.at(0);
15788- if (!issue) {
15789- throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`);
15790- }
15791- return this.getStagingDeployCashData(issue);
15792- });
15793- }
15794- /**
15795- * Takes in a GitHub issue object and returns the data we want.
15796- */
15797- static getStagingDeployCashData(issue) {
15798- try {
15799- const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g');
15800- const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', '');
15801- return {
15802- title: issue.title,
15803- url: issue.url,
15804- number: this.getIssueOrPullRequestNumberFromURL(issue.url),
15805- labels: issue.labels,
15806- PRList: this.getStagingDeployCashPRList(issue),
15807- PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue),
15808- deployBlockers: this.getStagingDeployCashDeployBlockers(issue),
15809- internalQAPRList: this.getStagingDeployCashInternalQA(issue),
15810- isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false,
15811- isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false,
15812- version,
15813- tag: `${version}-staging`,
15814- };
15815- }
15816- catch (exception) {
15817- throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`);
15818- }
15819- }
15820- /**
15821- * Parse the PRList and Internal QA section of the StagingDeployCash issue body.
15822- *
15823- * @private
15824- */
15825- static getStagingDeployCashPRList(issue) {
15826- let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null;
15827- if (PRListSection?.length !== 2) {
15828- // No PRs, return an empty array
15829- console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...');
15830- return [];
15831- }
15832- PRListSection = PRListSection[1];
15833- const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({
15834- url: match[2],
15835- number: Number.parseInt(match[3], 10),
15836- isVerified: match[1] === 'x',
15837- }));
15838- return PRList.sort((a, b) => a.number - b.number);
15839- }
15840- static getStagingDeployCashPRListMobileExpensify(issue) {
15841- let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null;
15842- if (mobileExpensifySection?.length !== 2) {
15843- return [];
15844- }
15845- mobileExpensifySection = mobileExpensifySection[1];
15846- const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({
15847- url: match[2],
15848- number: Number.parseInt(match[3], 10),
15849- isVerified: match[1] === 'x',
15850- }));
15851- return mobileExpensifyPRs.sort((a, b) => a.number - b.number);
15852- }
15853- /**
15854- * Parse DeployBlocker section of the StagingDeployCash issue body.
15855- *
15856- * @private
15857- */
15858- static getStagingDeployCashDeployBlockers(issue) {
15859- let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null;
15860- if (deployBlockerSection?.length !== 2) {
15861- return [];
15862- }
15863- deployBlockerSection = deployBlockerSection[1];
15864- const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({
15865- url: match[2],
15866- number: Number.parseInt(match[3], 10),
15867- isResolved: match[1] === 'x',
15868- }));
15869- return deployBlockers.sort((a, b) => a.number - b.number);
15870- }
15871- /**
15872- * Parse InternalQA section of the StagingDeployCash issue body.
15873- *
15874- * @private
15875- */
15876- static getStagingDeployCashInternalQA(issue) {
15877- let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null;
15878- if (internalQASection?.length !== 2) {
15879- return [];
15880- }
15881- internalQASection = internalQASection[1];
15882- const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({
15883- url: match[2].split('-').at(0)?.trim() ?? '',
15884- number: Number.parseInt(match[3], 10),
15885- isResolved: match[1] === 'x',
15886- }));
15887- return internalQAPRs.sort((a, b) => a.number - b.number);
15888- }
15889- /**
15890- * Generate the issue body and assignees for a StagingDeployCash.
15891- */
15892- static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) {
15893- return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr)))
15894- .then((data) => {
15895- const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : [];
15896- return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => {
15897- // The format of this map is following:
15898- // {
15899- // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv',
15900- // 'https://github.com/Expensify/App/pull/9642': 'mountiny'
15901- // }
15902- const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => {
15903- acc[url] = mergerLogin;
15904- return acc;
15905- }, {});
15906- console.log('Found the following Internal QA PRs:', internalQAPRMap);
15907- const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : [];
15908- console.log('Found the following NO QA PRs:', noQAPRs);
15909- const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]);
15910- const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b));
15911- const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b));
15912- const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b));
15913- // Tag version and comparison URL
15914- // eslint-disable-next-line max-len
15915- let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`;
15916- // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs
15917- if (sortedPRListMobileExpensify.length > 0) {
15918- issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`;
15919- }
15920- issueBody += '\r\n';
15921- // PR list
15922- if (sortedPRList.length > 0) {
15923- issueBody += '**This release contains changes from the following pull requests:**\r\n';
15924- for (const URL of sortedPRList) {
15925- issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]';
15926- issueBody += ` ${URL}\r\n`;
15927- }
15928- issueBody += '\r\n\r\n';
15929- }
15930- // Mobile-Expensify PR list
15931- if (sortedPRListMobileExpensify.length > 0) {
15932- issueBody += '**Mobile-Expensify PRs:**\r\n';
15933- for (const URL of sortedPRListMobileExpensify) {
15934- issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]';
15935- issueBody += ` ${URL}\r\n`;
15936- }
15937- issueBody += '\r\n\r\n';
15938- }
15939- // Internal QA PR list
15940- if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) {
15941- console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs);
15942- issueBody += '**Internal QA:**\r\n';
15943- for (const URL of Object.keys(internalQAPRMap)) {
15944- const merger = internalQAPRMap[URL];
15945- const mergerMention = `@${merger}`;
15946- issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `;
15947- issueBody += `${URL}`;
15948- issueBody += ` - ${mergerMention}`;
15949- issueBody += '\r\n';
15950- }
15951- issueBody += '\r\n\r\n';
15952- }
15953- // Deploy blockers
15954- if (deployBlockers.length > 0) {
15955- issueBody += '**Deploy Blockers:**\r\n';
15956- for (const URL of sortedDeployBlockers) {
15957- issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] ';
15958- issueBody += URL;
15959- issueBody += '\r\n';
15960- }
15961- issueBody += '\r\n\r\n';
15962- }
15963- if (chronologicalSection) {
15964- issueBody += chronologicalSection;
15965- issueBody += '\r\n\r\n';
15966- }
15967- issueBody += '**Deployer verifications:**';
15968- // eslint-disable-next-line max-len
15969- issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** 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).`;
15970- // eslint-disable-next-line max-len
15971- issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`;
15972- // eslint-disable-next-line max-len
15973- issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`;
15974- issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n';
15975- const issueAssignees = [...new Set(Object.values(internalQAPRMap))];
15976- const issue = { issueBody, issueAssignees };
15977- return issue;
15978- });
15979- })
15980- .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err));
15981- }
1598215767 /**
1598315768 * Fetch all pull requests given a list of PR numbers.
1598415769 */
@@ -16301,38 +16086,6 @@ class GithubUtils {
1630116086exports["default"] = GithubUtils;
1630216087
1630316088
16304- /***/ }),
16305-
16306- /***/ 7532:
16307- /***/ ((__unused_webpack_module, exports) => {
16308-
16309- "use strict";
16310-
16311- Object.defineProperty(exports, "__esModule", ({ value: true }));
16312- /**
16313- * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them.
16314- * It returns an array of items that are in the first array but not in the second array.
16315- */
16316- function arrayDifference(array1, array2) {
16317- return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c)));
16318- }
16319- exports["default"] = arrayDifference;
16320-
16321-
16322- /***/ }),
16323-
16324- /***/ 6497:
16325- /***/ ((__unused_webpack_module, exports) => {
16326-
16327- "use strict";
16328-
16329- Object.defineProperty(exports, "__esModule", ({ value: true }));
16330- exports.isEmptyObject = isEmptyObject;
16331- function isEmptyObject(obj) {
16332- return Object.keys(obj ?? {}).length === 0;
16333- }
16334-
16335-
1633616089/***/ }),
1633716090
1633816091/***/ 8534:
0 commit comments