Skip to content

Commit

Permalink
Merge branch 'main' into fix-displaying-disabled-tag-in-money-request…
Browse files Browse the repository at this point in the history
…-view
  • Loading branch information
FitseTLT committed Aug 23, 2024
2 parents c4adb96 + 6488908 commit 0866f7c
Show file tree
Hide file tree
Showing 394 changed files with 11,775 additions and 4,545 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
31 changes: 4 additions & 27 deletions .github/scripts/createHelpRedirects.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,6 @@ function checkCloudflareResult {
declare -a ITEMS_TO_ADD

while read -r line; do
# Split each line of the file into a source and destination so we can sanity check
# and compare against the current list.
read -r -a LINE_PARTS < <(echo "$line" | tr ',' ' ')
SOURCE_URL=${LINE_PARTS[0]}
DEST_URL=${LINE_PARTS[1]}

# Make sure the format of the line is as execpted.
if [[ "${#LINE_PARTS[@]}" -gt 2 ]]; then
error "Found a line with more than one comma: $line"
exit 1
fi

# Basic sanity checking to make sure that the source and destination are in expected
# subdomains.
if ! [[ $SOURCE_URL =~ ^https://(community|help)\.expensify\.com ]]; then
error "Found source URL that is not a communityDot or helpDot URL: $SOURCE_URL"
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"
exit 1
fi

info "Source: $SOURCE_URL and destination: $DEST_URL appear to be formatted correctly."

ITEMS_TO_ADD+=("$line")

# This line skips the first line in the csv because the first line is a header row.
Expand Down Expand Up @@ -83,6 +57,9 @@ done | jq -n '. |= [inputs]')

info "Adding redirects for $PUT_JSON"

# Dump $PUT_JSON into a file otherwise the curl request below will fail with too many arguments
echo "$PUT_JSON" > redirects.json

# We use PUT here instead of POST so that we replace the entire list in place. This has many benefits:
# 1. We don't have to check if items are already in the list, allowing this script to run faster
# 2. We can support deleting redirects this way by simply removing them from the list
Expand All @@ -93,7 +70,7 @@ info "Adding redirects for $PUT_JSON"
PUT_RESULT=$(curl -s --request PUT --url "https://api.cloudflare.com/client/v4/accounts/$ZONE_ID/rules/lists/$LIST_ID/items" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $CLOUDFLARE_LIST_TOKEN" \
--data "$PUT_JSON")
--data-binary @redirects.json)

checkCloudflareResult "$PUT_RESULT"
OPERATION_ID=$(echo "$PUT_RESULT" | jq -r .result.operation_id)
Expand Down
42 changes: 42 additions & 0 deletions .github/scripts/verifyRedirect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
# HelpDot - Verifies that redirects.csv does not have any duplicates
# Duplicate sourceURLs break redirection on cloudflare pages

source scripts/shellUtils.sh

declare -r REDIRECTS_FILE="docs/redirects.csv"
declare -a ITEMS_TO_ADD

declare -r RED='\033[0;31m'
declare -r GREEN='\033[0;32m'
Expand All @@ -22,5 +25,44 @@ if [[ DETECT_CYCLE_EXIT_CODE -eq 1 ]]; then
exit 1
fi

while read -r line; do
# Split each line of the file into a source and destination so we can sanity check
# and compare against the current list.
read -r -a LINE_PARTS < <(echo "$line" | tr ',' ' ')
SOURCE_URL=${LINE_PARTS[0]}
DEST_URL=${LINE_PARTS[1]}

# Make sure the format of the line is as expected.
if [[ "${#LINE_PARTS[@]}" -gt 2 ]]; then
error "Found a line with more than one comma: $line"
exit 1
fi

# Basic sanity checking to make sure that the source and destination are in expected
# subdomains.
if ! [[ $SOURCE_URL =~ ^https://(community|help)\.expensify\.com ]] || [[ $SOURCE_URL =~ \# ]]; then
error "Found source URL that is not a communityDot or helpDot URL, or contains a '#': $SOURCE_URL"
exit 1
fi

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

info "Source: $SOURCE_URL and destination: $DEST_URL appear to be formatted correctly."

ITEMS_TO_ADD+=("$line")

# This line skips the first line in the csv because the first line is a header row.
done <<< "$(tail +2 $REDIRECTS_FILE)"

# Sanity check that we should actually be running this and we aren't about to delete
# every single redirect.
if [[ "${#ITEMS_TO_ADD[@]}" -lt 1 ]]; then
error "No items found to add, why are we running?"
exit 1
fi

echo -e "${GREEN}The redirects.csv is valid!${NC}"
exit 0
Loading

0 comments on commit 0866f7c

Please sign in to comment.