Skip to content

Commit c015dce

Browse files
committed
merge main
2 parents 20cc74e + 3e16fb7 commit c015dce

1,244 files changed

Lines changed: 63720 additions & 38439 deletions

File tree

Some content is hidden

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

.claude/agents/code-inline-reviewer.md

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
name: code-inline-reviewer
44
description: Reviews code and creates inline comments for specific rule violations.
5-
tools: Glob, Grep, Read, TodoWrite, Bash, BashOutput, KillBash, mcp__github_inline_comment__create_inline_comment
5+
tools: Glob, Grep, Read, TodoWrite, Bash, BashOutput, KillBash
66
model: inherit
77
---
88

@@ -198,44 +198,12 @@ memo(ReportActionItem, (prevProps, nextProps) =>
198198
)
199199
```
200200

201-
---
202-
203-
### [PERF-6] Use specific properties as hook dependencies
204-
205-
- **Search patterns**: `useEffect`, `useMemo`, `useCallback` dependency arrays
206-
207-
- **Condition**: In `useEffect`, `useMemo`, and `useCallback`, specify individual object properties as dependencies instead of passing entire objects.
208-
- **Reasoning**: Passing entire objects as dependencies causes hooks to re-execute whenever any property changes, even unrelated ones. Specifying individual properties creates more granular dependency tracking, reducing unnecessary hook executions and improving performance predictability.
209-
210-
Good:
211-
212-
```tsx
213-
const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
214-
return {
215-
amountColumnSize: transactionItem.isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
216-
taxAmountColumnSize: transactionItem.isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
217-
dateColumnSize: transactionItem.shouldShowYear ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
218-
};
219-
}, [transactionItem.isAmountColumnWide, transactionItem.isTaxAmountColumnWide, transactionItem.shouldShowYear]);
220-
```
221-
222-
Bad:
223-
224-
```tsx
225-
const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
226-
return {
227-
amountColumnSize: transactionItem.isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
228-
taxAmountColumnSize: transactionItem.isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
229-
dateColumnSize: transactionItem.shouldShowYear ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
230-
};
231-
}, [transactionItem]);
232-
```
233-
234201
## Instructions
235202

236203
1. **First, get the list of changed files and their diffs:**
237204
- Use `gh pr diff` to see what actually changed in the PR
238205
- Focus ONLY on the changed lines, not the entire file
206+
- **CRITICAL**: Only create inline comments on lines that are part of the diff. Do NOT add comments to lines outside the diff, even if they contain violations. Comments on unchanged lines will fail to be created.
239207
2. **For analyzing changed files:**
240208
- **For large files (>5000 lines):** Use the Grep tool to search for specific violation patterns instead of reading the entire file. Focus grep searches on the changed portions shown in the diff.
241209
- **For smaller files:** You may read the full file using the Read tool
@@ -247,7 +215,7 @@ const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
247215
- `line`: Line number where the issue occurs
248216
- `body`: Concise and actionable description of the violation and fix, following the below Comment Format
249217
6. **Each comment must reference exactly one Rule ID.**
250-
7. **Output must consist exclusively of calls to mcp__github_inline_comment__create_inline_comment in the required format.** No other text, Markdown, or prose is allowed.
218+
7. **Output must consist exclusively of calls to createInlineComment.sh in the required format.** No other text, Markdown, or prose is allowed.
251219
8. **If no violations are found, add a reaction to the PR**:
252220
Add a 👍 (+1) reaction to the PR using the `addPrReaction` script (available in PATH from `.claude/scripts/`). The script takes ONLY the PR number as argument - it always adds a "+1" reaction, so do NOT pass any reaction type or emoji.
253221
9. **Add reaction if and only if**:
@@ -263,15 +231,13 @@ const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
263231

264232
## Tool Usage Example
265233

266-
For each violation, call the mcp__github_inline_comment__create_inline_comment tool like this.
267-
CRITICAL: **DO NOT** use the Bash tool for inline comments:
234+
For each violation, call the createInlineComment.sh script like this:
268235

236+
```bash
237+
createInlineComment.sh 'src/components/ReportActionsList.tsx' '<Body of the comment according to the Comment Format>' 128
269238
```
270-
mcp__github_inline_comment__create_inline_comment:
271-
path: 'src/components/ReportActionsList.tsx'
272-
line: 128
273-
body: '<Body of the comment according to the Comment Format>'
274-
```
239+
240+
**IMPORTANT**: Always use single quotes around the body argument to properly handle special characters and quotes.
275241

276242
If ZERO violations are found, use the Bash tool to add a reaction to the PR body:
277243

@@ -291,4 +257,4 @@ addPrReaction.sh <PR_NUMBER>
291257
<Suggested, specific fix preferably with a code snippet>
292258
```
293259

294-
**CRITICAL**: You must actually call the mcp__github_inline_comment__create_inline_comment tool for each violation. Don't just describe what you found - create the actual inline comments!
260+
**CRITICAL**: You must actually call the createInlineComment.sh script for each violation. Don't just describe what you found - create the actual inline comments!

.claude/commands/review-code-pr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
allowed-tools: Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(addPrReaction.sh:*),mcp__github_inline_comment__create_inline_comment
2+
allowed-tools: Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(addPrReaction.sh:*),Bash(createInlineComment.sh:*)
33
description: Review a code contribution pull request
44
---
55

.claude/scripts/addPrReaction.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ if [[ $# -lt 1 ]] || ! [[ "$1" =~ ^[0-9]+$ ]]; then
88
exit 1
99
fi
1010

11-
PR_NUMBER="$1"
12-
REPO="${GITHUB_REPOSITORY}"
11+
readonly PR_NUMBER="$1"
12+
readonly REPO="${GITHUB_REPOSITORY}"
1313

1414
gh api -X POST "/repos/$REPO/issues/$PR_NUMBER/reactions" -f content="+1"
1515

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
3+
# Secure proxy script to create an inline comment on a GitHub PR.
4+
set -eu
5+
6+
readonly ALLOWED_RULES_FILE="${GITHUB_WORKSPACE}/.claude/allowed-rules.txt"
7+
8+
# Print error and exit.
9+
die() {
10+
echo "Error: $*" >&2
11+
exit 1
12+
}
13+
14+
# Usage helper to avoid repeated text.
15+
usage() {
16+
die "Usage: $0 <path> <body> <line>"
17+
}
18+
19+
COMMENT_STATUS_REASON=""
20+
21+
# Ensure the comment body references an allowed rule tag.
22+
validate_rule() {
23+
local body="$1"
24+
local rule
25+
26+
[[ -f "$ALLOWED_RULES_FILE" ]] || die "Comment rejected: allowed rules file missing at $ALLOWED_RULES_FILE"
27+
28+
rule=$(echo "$body" | grep -oE '[A-Z]+-[0-9]+' | head -1 || true)
29+
[[ -n "$rule" ]] || die "Comment rejected: missing allowed rule reference (e.g. PERF-1)"
30+
31+
if grep -qF "$rule" "$ALLOWED_RULES_FILE"; then
32+
COMMENT_STATUS_REASON="rule $rule validated"
33+
return 0
34+
fi
35+
36+
die "Comment rejected: rule $rule not present in allowed list"
37+
}
38+
39+
readonly PATH_ARG="${1:-}"
40+
readonly BODY_ARG="${2:-}"
41+
readonly LINE_ARG="${3:-}"
42+
43+
[[ -z "$PR_NUMBER" ]] && die "Environment variable PR_NUMBER is required"
44+
[[ -z "$GITHUB_REPOSITORY" ]] && die "Environment variable GITHUB_REPOSITORY is required"
45+
[[ -z "$PATH_ARG" || -z "$BODY_ARG" || -z "$LINE_ARG" ]] && usage
46+
47+
validate_rule "$BODY_ARG"
48+
echo "Comment approved: $COMMENT_STATUS_REASON"
49+
50+
COMMIT_ID=$(gh api "/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" --jq '.head.sha')
51+
readonly COMMIT_ID
52+
53+
PAYLOAD=$(jq -n \
54+
--arg body "$BODY_ARG" \
55+
--arg path "$PATH_ARG" \
56+
--argjson line "$LINE_ARG" \
57+
--arg commit_id "$COMMIT_ID" \
58+
'{body: $body, path: $path, line: $line, side: "RIGHT", commit_id: $commit_id}')
59+
readonly PAYLOAD
60+
61+
gh api -X POST "/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments" \
62+
--input - <<< "$PAYLOAD" || exit 1

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ USE_REDUX_DEVTOOLS=false
1414
CAPTURE_METRICS=false
1515
ONYX_METRICS=false
1616
USE_THIRD_PARTY_SCRIPTS=false
17+
IS_EXPENSIFY_EMPLOYEE=false
1718

1819
EXPENSIFY_ACCOUNT_ID_ACCOUNTING=-1
1920
EXPENSIFY_ACCOUNT_ID_ACCOUNTS_PAYABLE=-1
@@ -42,3 +43,4 @@ GITHUB_TOKEN=YOUR_TOKEN
4243
OPENAI_API_KEY=YOUR_TOKEN
4344

4445
SENTRY_AUTH_TOKEN=SENTRY_AUTH_TOKEN
46+
SENTRY_ALLOW_FAILURE=true

.github/ISSUE_TEMPLATE/Standard.md

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ___
1010
**Version Number:**
1111
**Reproducible in staging?:**
1212
**Reproducible in production?:**
13-
**If this was caught during regression testing, add the test name, ID and link from TestRail:**
13+
**If this was caught during regression testing, add the test name, ID and link from BrowserStack:**
1414
**Email or phone of affected tester (no customers):**
1515
**Logs:** https://stackoverflow.com/c/expensify/questions/4856
1616
**Expensify/Expensify Issue URL:**
@@ -38,29 +38,8 @@ Select the officially supported platforms where the issue was reproduced:
3838
- [ ] iOS: mWeb Chrome
3939
- [ ] Windows: Chrome
4040
- [ ] MacOS: Chrome / Safari
41-
- [ ] MacOS: Desktop
42-
43-
<details>
44-
<summary>Platforms Tested:</summary>
45-
On which of our officially supported platforms was this issue tested:
46-
47-
- [ ] Android: App
48-
- [ ] Android: mWeb Chrome
49-
- [ ] iOS: App
50-
- [ ] iOS: mWeb Safari
51-
- [ ] iOS: mWeb Chrome
52-
- [ ] Windows: Chrome
53-
- [ ] MacOS: Chrome / Safari
54-
- [ ] MacOS: Desktop
55-
56-
</details>
5741

5842
## Screenshots/Videos
5943

60-
<details>
61-
<summary>Add any screenshot/video evidence</summary>
62-
63-
64-
</details>
6544

6645
[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Get Java version
2+
description: Reads the Java version from .java-version file
3+
4+
outputs:
5+
version:
6+
description: The Java version from .java-version file
7+
value: ${{ steps.getVersion.outputs.VERSION }}
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Get Java version
13+
id: getVersion
14+
shell: bash
15+
run: echo "VERSION=$(cat .java-version | tr -d '\n')" >> "$GITHUB_OUTPUT"

.github/actions/composite/setupNode/action.yml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ inputs:
66
description: "Indicates if node is set up for hybrid app"
77
required: false
88
default: 'false'
9+
IS_DESKTOP_BUILD:
10+
description: "Indicates if node is set up for desktop app"
11+
required: false
12+
default: 'false'
913

1014
outputs:
1115
cache-hit:
@@ -23,7 +27,9 @@ runs:
2327
with:
2428
node-version-file: '.nvmrc'
2529
cache: npm
26-
cache-dependency-path: normalized-package-lock.json
30+
cache-dependency-path: |
31+
normalized-package-lock.json
32+
desktop/package-lock.json
2733
2834
- id: cache-node-modules
2935
# v4
@@ -40,6 +46,14 @@ runs:
4046
path: Mobile-Expensify/node_modules
4147
key: ${{ runner.os }}-node-modules-${{ hashFiles('Mobile-Expensify/package-lock.json', 'Mobile-Expensify/patches/**') }}
4248

49+
- id: cache-desktop-node-modules
50+
if: inputs.IS_DESKTOP_BUILD == 'true'
51+
# v4
52+
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57
53+
with:
54+
path: desktop/node_modules
55+
key: ${{ runner.os }}-desktop-node-modules-${{ hashFiles('desktop/package-lock.json', 'desktop/patches/**') }}
56+
4357
- name: Remove ND node_modules if needed for hybrid app build
4458
if: inputs.IS_HYBRID_BUILD == 'true' && steps.cache-node-modules.outputs.cache-hit == 'true' && steps.cache-old-dot-node-modules.outputs.cache-hit != 'true'
4559
shell: bash
@@ -51,4 +65,12 @@ runs:
5165
with:
5266
timeout_minutes: 30
5367
max_attempts: 3
54-
command: npm ci
68+
command: npm ci
69+
70+
- name: Install node packages for desktop submodule
71+
if: inputs.IS_DESKTOP_BUILD == 'true' && steps.cache-desktop-node-modules.outputs.cache-hit != 'true'
72+
uses: nick-fields/retry@3f757583fb1b1f940bc8ef4bf4734c8dc02a5847
73+
with:
74+
timeout_minutes: 30
75+
max_attempts: 3
76+
command: cd desktop && npm ci

.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,21 @@ async function run(): Promise<IssuesCreateResponse | void> {
164164
});
165165

166166
// Then make sure we include any demoted or closed blockers as well, and just check them off automatically
167-
// eslint-disable-next-line unicorn/no-array-for-each
168-
currentChecklistData?.deployBlockers.forEach((deployBlocker) => {
167+
for (const deployBlocker of currentChecklistData?.deployBlockers ?? []) {
169168
const isResolved = deployBlockers.findIndex((openBlocker) => openBlocker.number === deployBlocker.number) < 0;
170169
deployBlockers.push({
171170
...deployBlocker,
172171
isResolved,
173172
});
174-
});
173+
}
175174

176175
// Include any existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list
177-
// eslint-disable-next-line unicorn/no-array-for-each
178-
currentChecklistData?.PRListMobileExpensify.forEach((existingPR) => {
176+
for (const existingPR of currentChecklistData?.PRListMobileExpensify ?? []) {
179177
const isAlreadyIncluded = PRListMobileExpensify.findIndex((pr) => pr.number === existingPR.number) >= 0;
180178
if (!isAlreadyIncluded) {
181179
PRListMobileExpensify.push(existingPR);
182180
}
183-
});
181+
}
184182

185183
const didVersionChange = newVersion !== currentChecklistData?.version;
186184
const stagingDeployCashBodyAndAssignees = await GithubUtils.generateStagingDeployCashBodyAndAssignees(

.github/actions/javascript/createOrUpdateStagingDeploy/index.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11674,22 +11674,20 @@ async function run() {
1167411674
};
1167511675
});
1167611676
// Then make sure we include any demoted or closed blockers as well, and just check them off automatically
11677-
// eslint-disable-next-line unicorn/no-array-for-each
11678-
currentChecklistData?.deployBlockers.forEach((deployBlocker) => {
11677+
for (const deployBlocker of currentChecklistData?.deployBlockers ?? []) {
1167911678
const isResolved = deployBlockers.findIndex((openBlocker) => openBlocker.number === deployBlocker.number) < 0;
1168011679
deployBlockers.push({
1168111680
...deployBlocker,
1168211681
isResolved,
1168311682
});
11684-
});
11683+
}
1168511684
// Include any existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list
11686-
// eslint-disable-next-line unicorn/no-array-for-each
11687-
currentChecklistData?.PRListMobileExpensify.forEach((existingPR) => {
11685+
for (const existingPR of currentChecklistData?.PRListMobileExpensify ?? []) {
1168811686
const isAlreadyIncluded = PRListMobileExpensify.findIndex((pr) => pr.number === existingPR.number) >= 0;
1168911687
if (!isAlreadyIncluded) {
1169011688
PRListMobileExpensify.push(existingPR);
1169111689
}
11692-
});
11690+
}
1169311691
const didVersionChange = newVersion !== currentChecklistData?.version;
1169411692
const stagingDeployCashBodyAndAssignees = await GithubUtils_1.default.generateStagingDeployCashBodyAndAssignees(newVersion, PRList.map((pr) => pr.url), PRListMobileExpensify.map((pr) => pr.url), PRList.filter((pr) => pr.isVerified).map((pr) => pr.url), PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.url), deployBlockers.map((blocker) => blocker.url), deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), currentChecklistData?.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.url), didVersionChange ? false : currentChecklistData.isFirebaseChecked, didVersionChange ? false : currentChecklistData.isGHStatusChecked);
1169511693
if (stagingDeployCashBodyAndAssignees) {

0 commit comments

Comments
 (0)