Skip to content

Commit

Permalink
Merge branch 'main' into fixOnboardingURLChange
Browse files Browse the repository at this point in the history
  • Loading branch information
tsa321 committed Aug 22, 2024
2 parents 0da5aec + ad7c1d5 commit 9b093c7
Show file tree
Hide file tree
Showing 292 changed files with 9,000 additions and 4,511 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,83 @@
import * as core from '@actions/core';
import * as github from '@actions/github';
import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types';
import {getJSONInput} from '@github/libs/ActionUtils';
import GithubUtils from '@github/libs/GithubUtils';
import GitUtils from '@github/libs/GitUtils';

type WorkflowRun = RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data']['workflow_runs'][number];

const BUILD_AND_DEPLOY_JOB_NAME_PREFIX = 'Build and deploy';

/**
* This function checks if a given release is a valid baseTag to get the PR list with `git log baseTag...endTag`.
*
* The rules are:
* - production deploys can only be compared with other production deploys
* - staging deploys can be compared with other staging deploys or production deploys.
* The reason is that the final staging release in each deploy cycle will BECOME a production release.
* For example, imagine a checklist is closed with version 9.0.20-6; that's the most recent staging deploy, but the release for 9.0.20-6 is now finalized, so it looks like a prod deploy.
* When 9.0.21-0 finishes deploying to staging, the most recent prerelease is 9.0.20-5. However, we want 9.0.20-6...9.0.21-0,
* NOT 9.0.20-5...9.0.21-0 (so that the PR CP'd in 9.0.20-6 is not included in the next checklist)
*/
async function isReleaseValidBaseForEnvironment(releaseTag: string, isProductionDeploy: boolean) {
if (!isProductionDeploy) {
return true;
}
const isPrerelease = (
await GithubUtils.octokit.repos.getReleaseByTag({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
tag: releaseTag,
})
).data.prerelease;
return !isPrerelease;
}

/**
* Was a given platformDeploy workflow run successful on at least one platform?
*/
async function wasDeploySuccessful(runID: number) {
const jobsForWorkflowRun = (
await GithubUtils.octokit.actions.listJobsForWorkflowRun({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// eslint-disable-next-line @typescript-eslint/naming-convention
run_id: runID,
filter: 'latest',
})
).data.jobs;
return jobsForWorkflowRun.some((job) => job.name.startsWith(BUILD_AND_DEPLOY_JOB_NAME_PREFIX) && job.conclusion === 'success');
}

/**
* This function checks if a given deploy workflow is a valid basis for comparison when listing PRs merged between two versions.
* It returns the reason a version should be skipped, or an empty string if the version should not be skipped.
*/
async function shouldSkipVersion(lastSuccessfulDeploy: WorkflowRun, inputTag: string, isProductionDeploy: boolean): Promise<string> {
if (!lastSuccessfulDeploy?.head_branch) {
// This should never happen. Just doing this to appease TS.
return '';
}

// we never want to compare a tag with itself. This check is necessary because prod deploys almost always have the same version as the last staging deploy.
// In this case, the next for wrong environment fails because the release that triggered that staging deploy is now finalized, so it looks like a prod deploy.
if (lastSuccessfulDeploy?.head_branch === inputTag) {
return `Same as input tag ${inputTag}`;
}
if (!(await isReleaseValidBaseForEnvironment(lastSuccessfulDeploy?.head_branch, isProductionDeploy))) {
return 'Was a staging deploy, we only want to compare with other production deploys';
}
if (!(await wasDeploySuccessful(lastSuccessfulDeploy.id))) {
return 'Was an unsuccessful deploy, nothing was deployed in that version';
}
return '';
}

async function run() {
try {
const inputTag = core.getInput('TAG', {required: true});
const isProductionDeploy = getJSONInput('IS_PRODUCTION_DEPLOY', {required: false}, false);
const isProductionDeploy = !!getJSONInput('IS_PRODUCTION_DEPLOY', {required: false}, false);
const deployEnv = isProductionDeploy ? 'production' : 'staging';

console.log(`Looking for PRs deployed to ${deployEnv} in ${inputTag}...`);
Expand All @@ -27,33 +97,26 @@ async function run() {

// Find the most recent deploy workflow targeting the correct environment, for which at least one of the build jobs finished successfully
let lastSuccessfulDeploy = completedDeploys.shift();
while (
lastSuccessfulDeploy?.head_branch &&
((
await GithubUtils.octokit.repos.getReleaseByTag({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
tag: lastSuccessfulDeploy.head_branch,
})
).data.prerelease === isProductionDeploy ||
!(
await GithubUtils.octokit.actions.listJobsForWorkflowRun({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// eslint-disable-next-line @typescript-eslint/naming-convention
run_id: lastSuccessfulDeploy.id,
filter: 'latest',
})
).data.jobs.some((job) => job.name.startsWith('Build and deploy') && job.conclusion === 'success'))
) {
console.log(`Deploy was not a success: ${lastSuccessfulDeploy.html_url}, looking at the next one`);
lastSuccessfulDeploy = completedDeploys.shift();
}

if (!lastSuccessfulDeploy) {
throw new Error('Could not find a prior successful deploy');
}

let reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy);
while (lastSuccessfulDeploy && reason) {
console.log(
`Deploy of tag ${lastSuccessfulDeploy.head_branch} was not valid as a base for comparison, looking at the next one. Reason: ${reason}`,
lastSuccessfulDeploy.html_url,
);
lastSuccessfulDeploy = completedDeploys.shift();

if (!lastSuccessfulDeploy) {
throw new Error('Could not find a prior successful deploy');
}

reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy);
}

const priorTag = lastSuccessfulDeploy.head_branch;
console.log(`Looking for PRs deployed to ${deployEnv} between ${priorTag} and ${inputTag}`);
const prList = await GitUtils.getPullRequestsMergedBetween(priorTag ?? '', inputTag);
Expand Down
85 changes: 68 additions & 17 deletions .github/actions/javascript/getDeployPullRequestList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11502,10 +11502,68 @@ const github = __importStar(__nccwpck_require__(5438));
const ActionUtils_1 = __nccwpck_require__(6981);
const GithubUtils_1 = __importDefault(__nccwpck_require__(9296));
const GitUtils_1 = __importDefault(__nccwpck_require__(1547));
const BUILD_AND_DEPLOY_JOB_NAME_PREFIX = 'Build and deploy';
/**
* This function checks if a given release is a valid baseTag to get the PR list with `git log baseTag...endTag`.
*
* The rules are:
* - production deploys can only be compared with other production deploys
* - staging deploys can be compared with other staging deploys or production deploys.
* The reason is that the final staging release in each deploy cycle will BECOME a production release.
* For example, imagine a checklist is closed with version 9.0.20-6; that's the most recent staging deploy, but the release for 9.0.20-6 is now finalized, so it looks like a prod deploy.
* When 9.0.21-0 finishes deploying to staging, the most recent prerelease is 9.0.20-5. However, we want 9.0.20-6...9.0.21-0,
* NOT 9.0.20-5...9.0.21-0 (so that the PR CP'd in 9.0.20-6 is not included in the next checklist)
*/
async function isReleaseValidBaseForEnvironment(releaseTag, isProductionDeploy) {
if (!isProductionDeploy) {
return true;
}
const isPrerelease = (await GithubUtils_1.default.octokit.repos.getReleaseByTag({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
tag: releaseTag,
})).data.prerelease;
return !isPrerelease;
}
/**
* Was a given platformDeploy workflow run successful on at least one platform?
*/
async function wasDeploySuccessful(runID) {
const jobsForWorkflowRun = (await GithubUtils_1.default.octokit.actions.listJobsForWorkflowRun({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// eslint-disable-next-line @typescript-eslint/naming-convention
run_id: runID,
filter: 'latest',
})).data.jobs;
return jobsForWorkflowRun.some((job) => job.name.startsWith(BUILD_AND_DEPLOY_JOB_NAME_PREFIX) && job.conclusion === 'success');
}
/**
* This function checks if a given deploy workflow is a valid basis for comparison when listing PRs merged between two versions.
* It returns the reason a version should be skipped, or an empty string if the version should not be skipped.
*/
async function shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy) {
if (!lastSuccessfulDeploy?.head_branch) {
// This should never happen. Just doing this to appease TS.
return '';
}
// we never want to compare a tag with itself. This check is necessary because prod deploys almost always have the same version as the last staging deploy.
// In this case, the next for wrong environment fails because the release that triggered that staging deploy is now finalized, so it looks like a prod deploy.
if (lastSuccessfulDeploy?.head_branch === inputTag) {
return `Same as input tag ${inputTag}`;
}
if (!(await isReleaseValidBaseForEnvironment(lastSuccessfulDeploy?.head_branch, isProductionDeploy))) {
return 'Was a staging deploy, we only want to compare with other production deploys';
}
if (!(await wasDeploySuccessful(lastSuccessfulDeploy.id))) {
return 'Was an unsuccessful deploy, nothing was deployed in that version';
}
return '';
}
async function run() {
try {
const inputTag = core.getInput('TAG', { required: true });
const isProductionDeploy = (0, ActionUtils_1.getJSONInput)('IS_PRODUCTION_DEPLOY', { required: false }, false);
const isProductionDeploy = !!(0, ActionUtils_1.getJSONInput)('IS_PRODUCTION_DEPLOY', { required: false }, false);
const deployEnv = isProductionDeploy ? 'production' : 'staging';
console.log(`Looking for PRs deployed to ${deployEnv} in ${inputTag}...`);
const completedDeploys = (await GithubUtils_1.default.octokit.actions.listWorkflowRuns({
Expand All @@ -11520,25 +11578,18 @@ async function run() {
.filter((workflowRun) => workflowRun.conclusion !== 'cancelled');
// Find the most recent deploy workflow targeting the correct environment, for which at least one of the build jobs finished successfully
let lastSuccessfulDeploy = completedDeploys.shift();
while (lastSuccessfulDeploy?.head_branch &&
((await GithubUtils_1.default.octokit.repos.getReleaseByTag({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
tag: lastSuccessfulDeploy.head_branch,
})).data.prerelease === isProductionDeploy ||
!(await GithubUtils_1.default.octokit.actions.listJobsForWorkflowRun({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// eslint-disable-next-line @typescript-eslint/naming-convention
run_id: lastSuccessfulDeploy.id,
filter: 'latest',
})).data.jobs.some((job) => job.name.startsWith('Build and deploy') && job.conclusion === 'success'))) {
console.log(`Deploy was not a success: ${lastSuccessfulDeploy.html_url}, looking at the next one`);
lastSuccessfulDeploy = completedDeploys.shift();
}
if (!lastSuccessfulDeploy) {
throw new Error('Could not find a prior successful deploy');
}
let reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy);
while (lastSuccessfulDeploy && reason) {
console.log(`Deploy of tag ${lastSuccessfulDeploy.head_branch} was not valid as a base for comparison, looking at the next one. Reason: ${reason}`, lastSuccessfulDeploy.html_url);
lastSuccessfulDeploy = completedDeploys.shift();
if (!lastSuccessfulDeploy) {
throw new Error('Could not find a prior successful deploy');
}
reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy);
}
const priorTag = lastSuccessfulDeploy.head_branch;
console.log(`Looking for PRs deployed to ${deployEnv} between ${priorTag} and ${inputTag}`);
const prList = await GitUtils_1.default.getPullRequestsMergedBetween(priorTag ?? '', inputTag);
Expand Down
5 changes: 3 additions & 2 deletions .github/actions/javascript/proposalPoliceComment/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17987,6 +17987,8 @@ function isCommentCreatedEvent(payload) {
}
// Main function to process the workflow event
async function run() {
// get date early, as soon as the workflow starts running
const date = new Date();
// Verify this is running for an expected webhook event
if (github_1.context.eventName !== CONST_1.default.EVENTS.ISSUE_COMMENT) {
throw new Error('ProposalPolice™ only supports the issue_comment webhook event');
Expand Down Expand Up @@ -18046,8 +18048,7 @@ async function run() {
// extract the text after [EDIT_COMMENT] from assistantResponse since this is a
// bot related action keyword
let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ')?.[1]?.replace('"', '');
// format the github's updated_at like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC
const date = new Date(payload.comment?.updated_at ?? '');
// format the date like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC
const formattedDate = `${date.toISOString()?.split('.')?.[0]?.replace('T', ' ')} UTC`;
extractedNotice = extractedNotice.replace('{updated_timestamp}', formattedDate);
console.log('ProposalPolice™ editing issue comment...', payload.comment.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ function isCommentCreatedEvent(payload: IssueCommentEvent): payload is IssueComm

// Main function to process the workflow event
async function run() {
// get date early, as soon as the workflow starts running
const date = new Date();
// Verify this is running for an expected webhook event
if (context.eventName !== CONST.EVENTS.ISSUE_COMMENT) {
throw new Error('ProposalPolice™ only supports the issue_comment webhook event');
Expand Down Expand Up @@ -87,8 +89,7 @@ async function run() {
// extract the text after [EDIT_COMMENT] from assistantResponse since this is a
// bot related action keyword
let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ')?.[1]?.replace('"', '');
// format the github's updated_at like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC
const date = new Date(payload.comment?.updated_at ?? '');
// format the date like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC
const formattedDate = `${date.toISOString()?.split('.')?.[0]?.replace('T', ' ')} UTC`;
extractedNotice = extractedNotice.replace('{updated_timestamp}', formattedDate);
console.log('ProposalPolice™ editing issue comment...', payload.comment.id);
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/createHelpRedirects.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ while read -r line; do
exit 1
fi

if ! [[ $DEST_URL =~ ^https://(help|use)\.expensify\.com ]]; then
error "Found destination URL that is not a helpDot or useDot URL: $DEST_URL"
if ! [[ $DEST_URL =~ ^https://(help|use|integrations)\.expensify\.com|^https://www\.expensify\.org ]]; then
error "Found destination URL that is not a supported URL: $DEST_URL"
exit 1
fi

Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/platformDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ env:
SHOULD_DEPLOY_PRODUCTION: ${{ github.event.action == 'released' }}

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}
group: ${{ github.workflow }}-${{ github.event.action }}
cancel-in-progress: true

jobs:
Expand Down Expand Up @@ -38,6 +38,7 @@ jobs:
secrets: inherit

android:
# WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly
name: Build and deploy Android
needs: validateActor
if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }}
Expand Down Expand Up @@ -122,6 +123,7 @@ jobs:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

desktop:
# WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly
name: Build and deploy Desktop
needs: validateActor
if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }}
Expand Down Expand Up @@ -165,6 +167,7 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}

iOS:
# WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly
name: Build and deploy iOS
needs: validateActor
if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }}
Expand Down Expand Up @@ -276,6 +279,7 @@ jobs:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

web:
# WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly
name: Build and deploy Web
needs: validateActor
if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }}
Expand Down Expand Up @@ -330,6 +334,7 @@ jobs:
- name: Verify staging deploy
if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
run: |
sleep 5
DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://staging.new.expensify.com/version.json | jq -r '.version')"
if [[ '${{ github.event.release.tag_name }}' != "$DOWNLOADED_VERSION" ]]; then
echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ github.event.release.tag_name }}. Something went wrong..."
Expand All @@ -339,6 +344,7 @@ jobs:
- name: Verify production deploy
if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
run: |
sleep 5
DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://new.expensify.com/version.json | jq -r '.version')"
if [[ '${{ github.event.release.tag_name }}' != "$DOWNLOADED_VERSION" ]]; then
echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ github.event.release.tag_name }}. Something went wrong..."
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/testBuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ jobs:
id: getHeadRef
run: |
set -e
gh pr checkout ${{ github.event.inputs.PULL_REQUEST_NUMBER }}
echo "REF=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
echo "REF=$(gh pr view ${{ github.event.inputs.PULL_REQUEST_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1009002004
versionName "9.0.20-4"
versionCode 1009002300
versionName "9.0.23-0"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const enEmojis: EmojisList = {
keywords: ['meh', 'face', 'unhappy'],
},
'🙄': {
keywords: ['eyes', 'face', 'rolling'],
keywords: ['eyes', 'face', 'rolling', 'eyeroll'],
},
'😬': {
keywords: ['face', 'grimace'],
Expand Down
Loading

0 comments on commit 9b093c7

Please sign in to comment.