Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/tienifr/App into fix/32292
Browse files Browse the repository at this point in the history
  • Loading branch information
tienifr committed Feb 15, 2024
2 parents 40b514f + 634f766 commit b5d3479
Show file tree
Hide file tree
Showing 775 changed files with 19,335 additions and 11,072 deletions.
2 changes: 1 addition & 1 deletion .github/scripts/createHelpRedirects.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function checkCloudflareResult {

if ! [[ "$RESULT_MESSAGE" == "true" ]]; then
ERROR_MESSAGE=$(echo "$RESULTS" | jq .errors)
error "Error calling Cloudfalre API: $ERROR_MESSAGE"
error "Error calling Cloudflare API: $ERROR_MESSAGE"
exit 1
fi
}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/deployExpensifyHelp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ jobs:
directory: ./docs/_site

- name: Setup Cloudflare CLI
run: pip3 install cloudflare
run: pip3 install cloudflare==2.17.0

- name: Purge Cloudflare cache
run: /home/runner/.local/bin/cli4 --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
env:
CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }}

Expand All @@ -66,7 +66,7 @@ jobs:
with:
token: ${{ secrets.OS_BOTIFY_TOKEN }}
body: ${{ format('A preview of your ExpensifyHelp changes have been deployed to {0} ⚡️', steps.deploy.outputs.alias) }}

- name: Reindex google search
if: ${{ github.event_name == 'push' }}
run : |
Expand Down
48 changes: 44 additions & 4 deletions .github/workflows/e2ePerformanceTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,26 +200,66 @@ jobs:
if: failure()
run: |
echo ${{ steps.schedule-awsdf-main.outputs.data }}
cat "./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/Test spec output.txt"
unzip "Customer Artifacts.zip" -d mainResults
cat ./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/debug.log
cat "./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/logcat.txt" || true
cat ./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/debug.log || true
cat "./mainResults/Host_Machine_Files/\$WORKING_DIRECTORY/Test spec output.txt" || true
- name: Announce failed workflow in Slack
if: failure()
uses: 8398a7/action-slack@v3
with:
status: custom
custom_payload: |
{
channel: '#expensify-margelo',
attachments: [{
color: 'danger',
text: `💥 ${process.env.AS_REPO} E2E Test run failed failed on <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}> workflow 💥`,
}]
}
env:
GITHUB_TOKEN: ${{ github.token }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

- name: Unzip AWS Device Farm results
if: ${{ always() }}
if: always()
run: unzip "Customer Artifacts.zip"

- name: Print AWS Device Farm run results
if: ${{ always() }}
if: always()
run: cat "./Host_Machine_Files/\$WORKING_DIRECTORY/output.md"

- name: Check if test failed, if so post the results and add the DeployBlocker label
id: checkIfRegressionDetected
run: |
if grep -q '🔴' ./output.md; then
# Create an output to the GH action that the test failed:
echo "performanceRegressionDetected=true" >> "$GITHUB_OUTPUT"
gh pr edit ${{ inputs.PR_NUMBER }} --add-label DeployBlockerCash
gh pr comment ${{ inputs.PR_NUMBER }} -F ./output.md
gh pr comment ${{ inputs.PR_NUMBER }} -b "@Expensify/mobile-deployers 📣 Please look into this performance regression as it's a deploy blocker."
else
echo "performanceRegressionDetected=false" >> "$GITHUB_OUTPUT"
echo '✅ no performance regression detected'
fi
env:
GITHUB_TOKEN: ${{ github.token }}

- name: 'Announce regression in Slack'
if: ${{ steps.checkIfRegressionDetected.outputs.performanceRegressionDetected == 'true' }}
uses: 8398a7/action-slack@v3
with:
status: custom
custom_payload: |
{
channel: '#newdot-performance',
attachments: [{
color: 'danger',
text: `🔴 Performance regression detected in PR ${{ inputs.PR_NUMBER }}\nDetected in <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}> workflow.`,
}]
}
env:
GITHUB_TOKEN: ${{ github.token }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
96 changes: 96 additions & 0 deletions .github/workflows/failureNotifier.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Notify on Workflow Failure

on:
workflow_run:
workflows: ["Process new code merged to main"]
types:
- completed

permissions:
issues: write

jobs:
notifyFailure:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: Fetch Workflow Run Jobs
id: fetch-workflow-jobs
uses: actions/github-script@v7
with:
script: |
const runId = "${{ github.event.workflow_run.id }}";
const jobsData = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId,
});
return jobsData.data;
- name: Process Each Failed Job
uses: actions/github-script@v7
with:
script: |
const jobs = ${{ steps.fetch-workflow-jobs.outputs.result }};
const headCommit = "${{ github.event.workflow_run.head_commit.id }}";
const prData = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: headCommit,
});
const pr = prData.data[0];
const prLink = pr.html_url;
const prAuthor = pr.user.login;
const prMerger = "${{ github.event.workflow_run.actor.login }}";
const failureLabel = 'Workflow Failure';
for (let i = 0; i < jobs.total_count; i++) {
if (jobs.jobs[i].conclusion == 'failure') {
const jobName = jobs.jobs[i].name;
const jobLink = jobs.jobs[i].html_url;
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: failureLabel,
state: 'open'
});
const existingIssue = issues.data.find(issue => issue.title.includes(jobName));
if (!existingIssue) {
const annotations = await github.rest.checks.listAnnotations({
owner: context.repo.owner,
repo: context.repo.repo,
check_run_id: jobs.jobs[i].id,
});
let errorMessage = "";
for(let j = 0; j < annotations.data.length; j++) {
errorMessage += annotations.data[j].annotation_level + ": ";
errorMessage += annotations.data[j].message + "\n";
}
const issueTitle = `Investigate workflow job failing on main: ${ jobName }`;
const issueBody = `🚨 **Failure Summary** 🚨:\n\n` +
`- **📋 Job Name**: [${ jobName }](${ jobLink })\n` +
`- **🔧 Failure in Workflow**: Process new code merged to main\n` +
`- **🔗 Triggered by PR**: [PR Link](${ prLink })\n` +
`- **👤 PR Author**: @${ prAuthor }\n` +
`- **🤝 Merged by**: @${ prMerger }\n` +
`- **🐛 Error Message**: \n ${errorMessage}\n\n` +
`⚠️ **Action Required** ⚠️:\n\n` +
`🛠️ A recent merge appears to have caused a failure in the job named [${ jobName }](${ jobLink }).\n` +
`This issue has been automatically created and labeled with \`${ failureLabel }\` for investigation. \n\n` +
`👀 **Please look into the following**:\n` +
`1. **Why the PR caused the job to fail?**\n` +
`2. **Address any underlying issues.**\n\n` +
`🐛 We appreciate your help in squashing this bug!`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issueTitle,
body: issueBody,
labels: [failureLabel, 'Daily'],
assignees: [prMerger]
});
}
}
}
6 changes: 3 additions & 3 deletions .github/workflows/platformDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ jobs:
uses: ./.github/actions/composite/setupNode

- name: Setup Cloudflare CLI
run: pip3 install cloudflare
run: pip3 install cloudflare==2.17.0

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
Expand Down Expand Up @@ -338,13 +338,13 @@ jobs:

- name: Purge production Cloudflare cache
if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
run: /home/runner/.local/bin/cli4 --delete hosts=["new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
env:
CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }}

- name: Purge staging Cloudflare cache
if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
run: /home/runner/.local/bin/cli4 --delete hosts=["staging.new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["staging.new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
env:
CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }}

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/preDeploy.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Reminder: If this workflow's name changes, update the name in the dependent workflow at .github/workflows/failureNotifier.yml.
name: Process new code merged to main

on:
Expand Down
2 changes: 1 addition & 1 deletion .storybook/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = ({config}) => {
config.resolve.alias = {
'react-native-config': 'react-web-config',
'react-native$': 'react-native-web',
'@react-native-community/netinfo': path.resolve(__dirname, '../__mocks__/@react-native-community/netinfo.js'),
'@react-native-community/netinfo': path.resolve(__dirname, '../__mocks__/@react-native-community/netinfo.ts'),
'@react-navigation/native': path.resolve(__dirname, '../__mocks__/@react-navigation/native'),

// Module alias support for storybook files, coping from `webpack.common.js`
Expand Down
112 changes: 112 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* [Debugging](#debugging)
* [App Structure and Conventions](#app-structure-and-conventions)
* [Philosophy](#Philosophy)
* [Security](#Security)
* [Internationalization](#Internationalization)
* [Deploying](#deploying)

Expand Down Expand Up @@ -395,6 +396,117 @@ This application is built with the following principles.

----

# Security
Updated rules for managing members across all types of chats in New Expensify.

- **Nobody can leave or be removed from something they were automatically added to. For example:**

- DM members can't leave or be removed from their DMs
- Members can't leave or be removed from their own workspace chats
- Admins can't leave or be removed from workspace chats
- Members can't leave or be removed from the #announce room
- Admins can't leave or be removed from #admins
- Domain members can't leave or be removed from their domain chat
- Report submitters can't leave or be removed from their reports
- Report managers can't leave or be removed from their reports
- Group owners cannot be removed from their groups - they need to transfer ownership first
- **Excepting the above, admins can remove anyone. For example:**
- Group admins can remove other group admins, as well as group members
- Workspace admins can remove other workspace admins, as well as workspace members, and invited guests
- **Excepting the above, members can remove guests. For example:**
- Workspace members can remove non-workspace guests.
- **Excepting the above, anybody can remove themselves from any object**

1. ### DM
| | Member
| :---: | :---:
| **Invite** | ❌
| **Remove** | ❌
| **Leave** | ❌
| **Can be removed** | ❌
- DM always has two participants. None of the participant can leave or be removed from the DM. Also no additional member can be invited to the chat.

2. ### Workspace
1. #### Workspace
| | Creator | Member(Employee/User) | Admin | Auditor?
| :---: | :---: | :---: | :---: | :---:
| **Invite** | ✅ | ❌ | ✅ | ❌
| **Remove** | ✅ | ❌ | ✅ | ❌
| **Leave** | ❌ | ✅ | ❌ | ✅
| **Can be removed** | ❌ | ✅ | ✅ | ✅

- Creator can't leave or be removed from their own workspace
- Admins can't leave from the workspace
- Admins can remove other workspace admins, as well as workspace members, and invited guests
- Creator can remove other workspace admins, as well as workspace members, and invited guests
- Members and Auditors cannot invite or remove anyone from the workspace

2. #### Workspace #announce room
| | Member(Employee/User) | Admin | Auditor?
| :---: | :---: | :---: | :---:
| **Invite** | ❌ | ❌ | ❌
| **Remove** | ❌ | ❌ | ❌
| **Leave** | ❌ | ❌ | ❌
| **Can be removed** ||||

- No one can leave or be removed from the #announce room

3. #### Workspace #admin room
| | Admin |
| :---: | :---:
| **Invite** | ❌
| **Remove** | ❌
| **Leave** | ❌
| **Can be removed** | ❌

- Admins can't leave or be removed from #admins

4. #### Workspace rooms
| | Creator | Member | Guest(outside of the workspace)
| :---: | :---: | :---: | :---:
| **Invite** | ✅ | ✅ | ✅
| **Remove** | ✅ | ✅ | ❌
| **Leave** | ✅ | ✅ | ✅
| **Can be removed** | ✅ | ✅ | ✅

- Everyone can be removed/can leave from the room including creator
- Guests are not able to remove anyone from the room

4. #### Workspace chats
| | Admin | Member(default) | Member(invited)
| :---: | :---: | :---: | :---:
| **Invite** | ✅ | ✅ | ❌
| **Remove** | ✅ | ✅ | ❌
| **Leave** | ❌ | ❌ | ✅
| **Can be removed** | ❌ | ❌ | ✅

- Admins are not able to leave/be removed from the workspace chat
- Default members(automatically invited) are not able to leave/be removed from the workspace chat
- Invited members(invited by members) are not able to invite or remove from the workspace chat
- Invited members(invited by members) are able to leave the workspace chat
- Default members and admins are able to remove invited members

3. ### Domain chat
| | Member
| :---: | :---:
| **Remove** | ❌
| **Leave** | ❌
| **Can be removed** | ❌

- Domain members can't leave or be removed from their domain chat

4. ### Reports
| | Submitter | Manager
| :---: | :---: | :---:
| **Remove** | ❌ | ❌
| **Leave** | ❌ | ❌
| **Can be removed** | ❌ | ❌

- Report submitters can't leave or be removed from their reports (eg, if they are the report.accountID)
- Report managers can't leave or be removed from their reports (eg, if they are the report.managerID)

----

# Internationalization
This application is built with Internationalization (I18n) / Localization (L10n) support, so it's important to always
localize the following types of data when presented to the user (even accessibility texts that are not rendered):
Expand Down
1 change: 0 additions & 1 deletion __mocks__/@react-native-async-storage/async-storage.js

This file was deleted.

5 changes: 0 additions & 5 deletions __mocks__/@react-native-camera-roll/camera-roll.js

This file was deleted.

15 changes: 15 additions & 0 deletions __mocks__/@react-native-camera-roll/camera-roll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type {CameraRoll} from '@react-native-camera-roll/camera-roll';

type CameraRollMock = {
CameraRoll: {
save: typeof CameraRoll.save;
};
};

const cameraRollMock: CameraRollMock = {
CameraRoll: {
save: jest.fn(),
},
};

export default cameraRollMock;
3 changes: 3 additions & 0 deletions __mocks__/@react-native-clipboard/clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import MockClipboard from '@react-native-clipboard/clipboard/jest/clipboard-mock';

export default MockClipboard;
Loading

0 comments on commit b5d3479

Please sign in to comment.