Skip to content

Commit cf3ce5b

Browse files
committed
Merge remote-tracking branch 'upstream/main' into KJ21-ENG/90933-admin-invite-onboarding-v3
2 parents 9a2c433 + c38b401 commit cf3ce5b

602 files changed

Lines changed: 19868 additions & 10821 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.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Upload build to Sentry for size analysis
2+
description: |
3+
Download a platform binary and upload it to Sentry for size analysis. The binary is sourced
4+
from either the current workflow run's artifacts (cherry-pick prod deploys, which build natively)
5+
or the matching staging GitHub Release (re-promotion prod deploys, which do not build natively).
6+
7+
inputs:
8+
platform:
9+
description: "Platform label used in the GH Actions notice (e.g. 'Android' or 'iOS')"
10+
required: true
11+
asset-name:
12+
description: "Filename of the binary on disk and as the GH Release asset (e.g. 'Expensify-release.aab')"
13+
required: true
14+
artifact-name:
15+
description: "Name of the GH Actions artifact to download for cherry-pick deploys (e.g. 'androidBuild-artifact')"
16+
required: true
17+
staging-release-tag:
18+
description: "Tag of the staging GH Release to download from for re-promotion deploys (e.g. '9.4.13-0-staging')"
19+
required: true
20+
is-cherry-pick:
21+
description: "'true' when this deploy is a cherry-pick (binary is in the current run); 'false' otherwise (binary is on the staging release)"
22+
required: true
23+
24+
outputs:
25+
SENTRY_URL:
26+
description: URL to the Sentry size analysis for the uploaded build (empty if upload failed or quota exhausted)
27+
value: ${{ steps.upload.outputs.SENTRY_URL }}
28+
29+
runs:
30+
using: composite
31+
steps:
32+
- name: Setup Node
33+
uses: ./.github/actions/composite/setupNode
34+
35+
- name: Download binary from current workflow run (cherry-pick path)
36+
if: ${{ inputs.is-cherry-pick == 'true' }}
37+
continue-on-error: true
38+
# v7
39+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
40+
with:
41+
name: ${{ inputs.artifact-name }}
42+
path: ./
43+
44+
- name: Download binary from the staging GitHub Release (re-promotion path)
45+
if: ${{ inputs.is-cherry-pick != 'true' }}
46+
continue-on-error: true
47+
shell: bash
48+
env:
49+
TAG: ${{ inputs.staging-release-tag }}
50+
ASSET: ${{ inputs.asset-name }}
51+
run: |
52+
gh release download "$TAG" --pattern "$ASSET" --output "$ASSET"
53+
[ -s "$ASSET" ] || { echo "::error::Failed to obtain $ASSET from $TAG"; exit 1; }
54+
55+
- name: Upload to Sentry
56+
id: upload
57+
if: ${{ hashFiles(inputs.asset-name) != '' }}
58+
continue-on-error: true
59+
shell: bash
60+
env:
61+
ASSET: ${{ inputs.asset-name }}
62+
PLATFORM: ${{ inputs.platform }}
63+
run: |
64+
OUTPUT=$(npx sentry-cli build upload "$ASSET" \
65+
--org expensify --project app --build-configuration Release --log-level info 2>&1)
66+
echo "$OUTPUT"
67+
SENTRY_URL=$(echo "$OUTPUT" | grep -oE 'https://expensify\.sentry\.io/[^ )"}]+' | head -1)
68+
if [ -n "$SENTRY_URL" ]; then
69+
echo "::notice::${PLATFORM} Sentry size analysis: $SENTRY_URL"
70+
echo "SENTRY_URL=$SENTRY_URL" >> "$GITHUB_OUTPUT"
71+
fi

.github/workflows/buildAndroid.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ on:
3535
ROCK_ARTIFACT_URL:
3636
description: URL to download the ad-hoc build artifact (adhoc only)
3737
value: ${{ jobs.build.outputs.ROCK_ARTIFACT_URL }}
38-
SENTRY_URL:
39-
description: URL to Sentry size analysis
40-
value: ${{ jobs.build.outputs.SENTRY_URL }}
4138
AAB_FILENAME:
4239
description: Filename of the AAB artifact (empty if no AAB was produced)
4340
value: ${{ jobs.build.outputs.AAB_FILENAME }}
@@ -60,7 +57,6 @@ jobs:
6057
outputs:
6158
VERSION_CODE: ${{ steps.getAndroidVersion.outputs.VERSION_CODE }}
6259
ROCK_ARTIFACT_URL: ${{ steps.set-artifact-url.outputs.ARTIFACT_URL }}
63-
SENTRY_URL: ${{ steps.sentry-upload.outputs.SENTRY_URL }}
6460
AAB_FILENAME: ${{ steps.collectArtifacts.outputs.AAB_FILENAME }}
6561
APK_FILENAME: ${{ steps.collectArtifacts.outputs.APK_FILENAME }}
6662
SOURCEMAP_FILENAME: index.android.bundle.map
@@ -195,22 +191,6 @@ jobs:
195191
if: ${{ inputs.variant == 'Adhoc' }}
196192
run: echo "ARTIFACT_URL=$ARTIFACT_URL" >> "$GITHUB_OUTPUT"
197193

198-
- name: Upload Android build to Sentry for size analysis
199-
id: sentry-upload
200-
if: ${{ inputs.variant == 'Release' && env.ARTIFACT_PATH != '' }}
201-
continue-on-error: true
202-
timeout-minutes: 5
203-
run: |
204-
OUTPUT=$(npx sentry-cli build upload "$ARTIFACT_PATH" --org expensify --project app --build-configuration Release --log-level debug 2>&1)
205-
echo "$OUTPUT"
206-
SENTRY_URL=$(echo "$OUTPUT" | grep -oE 'https://expensify\.sentry\.io/[^ ]+' | head -1)
207-
if [ -n "$SENTRY_URL" ]; then
208-
echo "::notice::Android Sentry size analysis: $SENTRY_URL"
209-
echo "SENTRY_URL=$SENTRY_URL" >> "$GITHUB_OUTPUT"
210-
fi
211-
env:
212-
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
213-
214194
- name: Upload Gradle profile report
215195
if: always()
216196
# v6

.github/workflows/buildIOS.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ on:
3535
ROCK_ARTIFACT_URL:
3636
description: URL to download the ad-hoc build artifact (adhoc only)
3737
value: ${{ jobs.build.outputs.ROCK_ARTIFACT_URL }}
38-
SENTRY_URL:
39-
description: URL to Sentry size analysis
40-
value: ${{ jobs.build.outputs.SENTRY_URL }}
4138
IPA_FILENAME:
4239
description: Filename of the IPA artifact produced by the build
4340
value: ${{ jobs.build.outputs.IPA_FILENAME }}
@@ -58,7 +55,6 @@ jobs:
5855
outputs:
5956
IOS_VERSION: ${{ steps.getIOSVersion.outputs.IOS_VERSION }}
6057
ROCK_ARTIFACT_URL: ${{ steps.set-artifact-url.outputs.ARTIFACT_URL }}
61-
SENTRY_URL: ${{ steps.sentry-upload.outputs.SENTRY_URL }}
6258
IPA_FILENAME: ${{ steps.set-ipa-filename.outputs.IPA_FILENAME }}
6359
DSYM_FILENAME: ${{ steps.set-ipa-filename.outputs.DSYM_FILENAME }}
6460
SOURCEMAP_FILENAME: main.jsbundle.map
@@ -244,22 +240,6 @@ jobs:
244240
comment-bot: false
245241
custom-identifier: ${{ steps.computeIdentifier.outputs.IDENTIFIER }}
246242

247-
- name: Upload iOS build to Sentry for size analysis
248-
id: sentry-upload
249-
if: ${{ inputs.variant == 'Release' && env.ARTIFACT_PATH != '' }}
250-
continue-on-error: true
251-
timeout-minutes: 5
252-
run: |
253-
OUTPUT=$(npx sentry-cli build upload "$ARTIFACT_PATH" --org expensify --project app --build-configuration Release --log-level debug 2>&1)
254-
echo "$OUTPUT"
255-
SENTRY_URL=$(echo "$OUTPUT" | grep -oE 'https://expensify\.sentry\.io/[^ ]+' | head -1)
256-
if [ -n "$SENTRY_URL" ]; then
257-
echo "::notice::iOS Sentry size analysis: $SENTRY_URL"
258-
echo "SENTRY_URL=$SENTRY_URL" >> "$GITHUB_OUTPUT"
259-
fi
260-
env:
261-
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
262-
263243
- name: Set artifact URL output
264244
id: set-artifact-url
265245
if: ${{ inputs.variant == 'Adhoc' }}

.github/workflows/deploy.yml

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,12 @@ jobs:
918918
--target "${{ env.DEPLOY_ENV }}"; then
919919
break
920920
fi
921+
922+
# A transient GitHub error during create doesn't necessarily mean the release failed; it may have been created anyway, so a later "already exists" error means it's present.
923+
if gh release view "${{ needs.prep.outputs.TAG }}" --repo "${{ github.repository }}" &> /dev/null; then
924+
echo "Release ${{ needs.prep.outputs.TAG }} exists despite a create error (likely a transient 502), treating as success."
925+
break
926+
fi
921927
if [[ $i -lt $CREATE_MAX_RETRIES ]]; then
922928
echo "Failed to create release. Retrying in 3 seconds... ($((CREATE_MAX_RETRIES - i)) attempts left)"
923929
sleep 3
@@ -999,6 +1005,68 @@ jobs:
9991005
}]
10001006
}
10011007
1008+
# Upload the production-bound binary to Sentry for size analysis.
1009+
# - Cherry-pick deploys build native in this run, so we pull the .aab/.ipa from the build artifact.
1010+
# - Re-promotion deploys don't build native, so we download from the matching `<TAG>-staging`
1011+
# GitHub Release populated during the staging deploy of that version.
1012+
androidUploadSentry:
1013+
name: Upload Android build to Sentry for size analysis
1014+
needs: [prep, androidBuild, checkDeploymentSuccess]
1015+
runs-on: blacksmith-2vcpu-ubuntu-2404
1016+
if: >-
1017+
${{ always()
1018+
&& needs.prep.outputs.DEPLOY_ENV == 'production'
1019+
&& fromJSON(needs.checkDeploymentSuccess.outputs.IS_RELEASE_READY) }}
1020+
continue-on-error: true
1021+
timeout-minutes: 10
1022+
outputs:
1023+
SENTRY_URL: ${{ steps.sentry-upload.outputs.SENTRY_URL }}
1024+
steps:
1025+
- name: Checkout
1026+
uses: useblacksmith/checkout@c9796daa2a4bdebdab5bd16be2c09a70cd4e1121 # v1
1027+
1028+
- name: Upload to Sentry for size analysis
1029+
id: sentry-upload
1030+
uses: ./.github/actions/composite/uploadSentrySizeAnalysis
1031+
env:
1032+
GH_TOKEN: ${{ github.token }}
1033+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
1034+
with:
1035+
platform: Android
1036+
asset-name: Expensify-release.aab
1037+
artifact-name: androidBuild-artifact
1038+
staging-release-tag: ${{ needs.prep.outputs.TAG }}-staging
1039+
is-cherry-pick: ${{ needs.prep.outputs.IS_CHERRY_PICK }}
1040+
1041+
iosUploadSentry:
1042+
name: Upload iOS build to Sentry for size analysis
1043+
needs: [prep, iosBuild, checkDeploymentSuccess]
1044+
runs-on: blacksmith-2vcpu-ubuntu-2404
1045+
if: >-
1046+
${{ always()
1047+
&& needs.prep.outputs.DEPLOY_ENV == 'production'
1048+
&& fromJSON(needs.checkDeploymentSuccess.outputs.IS_RELEASE_READY) }}
1049+
continue-on-error: true
1050+
timeout-minutes: 10
1051+
outputs:
1052+
SENTRY_URL: ${{ steps.sentry-upload.outputs.SENTRY_URL }}
1053+
steps:
1054+
- name: Checkout
1055+
uses: useblacksmith/checkout@c9796daa2a4bdebdab5bd16be2c09a70cd4e1121 # v1
1056+
1057+
- name: Upload to Sentry for size analysis
1058+
id: sentry-upload
1059+
uses: ./.github/actions/composite/uploadSentrySizeAnalysis
1060+
env:
1061+
GH_TOKEN: ${{ github.token }}
1062+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
1063+
with:
1064+
platform: iOS
1065+
asset-name: Expensify.ipa
1066+
artifact-name: iosBuild-artifact
1067+
staging-release-tag: ${{ needs.prep.outputs.TAG }}-staging
1068+
is-cherry-pick: ${{ needs.prep.outputs.IS_CHERRY_PICK }}
1069+
10021070
# Why is this necessary for CP-to-prod? Consider this scenario:
10031071
# 1. You close a checklist and we create a new staging version `9.0.34-0` and a new checklist
10041072
# 2. You then CP a PR to production, and in the process create `9.0.35-0`
@@ -1068,13 +1136,13 @@ jobs:
10681136
postGithubComments:
10691137
uses: ./.github/workflows/postDeployComments.yml
10701138
if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_RELEASE_READY) }}
1071-
needs: [prep, checkDeploymentSuccess, createRelease, androidBuild, iosBuild]
1139+
needs: [prep, checkDeploymentSuccess, createRelease, androidBuild, iosBuild, androidUploadSentry, iosUploadSentry]
10721140
secrets: inherit
10731141
with:
10741142
version: ${{ needs.prep.outputs.APP_VERSION }}
10751143
env: ${{ needs.prep.outputs.DEPLOY_ENV }}
10761144
android: ${{ needs.checkDeploymentSuccess.outputs.ANDROID_RESULT }}
10771145
ios: ${{ needs.checkDeploymentSuccess.outputs.IOS_RESULT }}
10781146
web: ${{ needs.checkDeploymentSuccess.outputs.WEB_RESULT }}
1079-
android_sentry_url: ${{ needs.androidBuild.outputs.SENTRY_URL }}
1080-
ios_sentry_url: ${{ needs.iosBuild.outputs.SENTRY_URL }}
1147+
android_sentry_url: ${{ needs.androidUploadSentry.outputs.SENTRY_URL }}
1148+
ios_sentry_url: ${{ needs.iosUploadSentry.outputs.SENTRY_URL }}

Mobile-Expensify

__mocks__/d3-scale.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/* eslint-disable */
2+
// @ts-nocheck
3+
/**
4+
* CJS-compatible mock of d3-scale for Jest.
5+
*
6+
* d3-scale is ESM-only ("type": "module") so it cannot be require()'d in Jest's CJS runtime.
7+
* Verbatim copy of the relevant parts of d3-array/src/ticks.js and d3-scale/src/linear.js.
8+
*/
9+
10+
// d3-array/src/ticks.js
11+
const e10 = Math.sqrt(50),
12+
e5 = Math.sqrt(10),
13+
e2 = Math.sqrt(2);
14+
15+
function tickSpec(start, stop, count) {
16+
const step = (stop - start) / Math.max(0, count),
17+
power = Math.floor(Math.log10(step)),
18+
error = step / Math.pow(10, power),
19+
factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1;
20+
let i1, i2, inc;
21+
if (power < 0) {
22+
inc = Math.pow(10, -power) / factor;
23+
i1 = Math.round(start * inc);
24+
i2 = Math.round(stop * inc);
25+
if (i1 / inc < start) ++i1;
26+
if (i2 / inc > stop) --i2;
27+
inc = -inc;
28+
} else {
29+
inc = Math.pow(10, power) * factor;
30+
i1 = Math.round(start / inc);
31+
i2 = Math.round(stop / inc);
32+
if (i1 * inc < start) ++i1;
33+
if (i2 * inc > stop) --i2;
34+
}
35+
if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2);
36+
return [i1, i2, inc];
37+
}
38+
39+
function ticks(start, stop, count) {
40+
((stop = +stop), (start = +start), (count = +count));
41+
if (!(count > 0)) return [];
42+
if (start === stop) return [start];
43+
const reverse = stop < start,
44+
[i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count);
45+
if (!(i2 >= i1)) return [];
46+
const n = i2 - i1 + 1,
47+
ticks = new Array(n);
48+
if (reverse) {
49+
if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) / -inc;
50+
else for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) * inc;
51+
} else {
52+
if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) / -inc;
53+
else for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) * inc;
54+
}
55+
return ticks;
56+
}
57+
58+
function tickIncrement(start, stop, count) {
59+
((stop = +stop), (start = +start), (count = +count));
60+
return tickSpec(start, stop, count)[2];
61+
}
62+
63+
// d3-scale/src/linear.js
64+
function scaleLinear() {
65+
let domain = [0, 1];
66+
67+
const scale = {
68+
domain(d) {
69+
if (!d) return domain;
70+
domain = [...d];
71+
return scale;
72+
},
73+
nice(count = 10) {
74+
var d = domain;
75+
var i0 = 0;
76+
var i1 = d.length - 1;
77+
var start = d[i0];
78+
var stop = d[i1];
79+
var prestep;
80+
var step;
81+
var maxIter = 10;
82+
83+
if (stop < start) {
84+
((step = start), (start = stop), (stop = step));
85+
((step = i0), (i0 = i1), (i1 = step));
86+
}
87+
88+
while (maxIter-- > 0) {
89+
step = tickIncrement(start, stop, count);
90+
if (step === prestep) {
91+
d[i0] = start;
92+
d[i1] = stop;
93+
domain = d;
94+
return scale;
95+
} else if (step > 0) {
96+
start = Math.floor(start / step) * step;
97+
stop = Math.ceil(stop / step) * step;
98+
} else if (step < 0) {
99+
start = Math.ceil(start * step) / step;
100+
stop = Math.floor(stop * step) / step;
101+
} else {
102+
break;
103+
}
104+
prestep = step;
105+
}
106+
return scale;
107+
},
108+
ticks(count = 10) {
109+
var d = domain;
110+
return ticks(d[0], d[d.length - 1], count);
111+
},
112+
};
113+
114+
return scale;
115+
}
116+
117+
module.exports = {scaleLinear};

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ android {
111111
minSdkVersion rootProject.ext.minSdkVersion
112112
targetSdkVersion rootProject.ext.targetSdkVersion
113113
multiDexEnabled rootProject.ext.multiDexEnabled
114-
versionCode 1009041703
115-
versionName "9.4.17-3"
114+
versionCode 1009042102
115+
versionName "9.4.21-2"
116116
// Supported language variants must be declared here to avoid from being removed during the compilation.
117117
// This also helps us to not include unnecessary language variants in the APK.
118118
resConfigs "en", "es"

assets/css/fonts.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070

7171
@font-face {
7272
font-family: Windows Segoe UI Emoji;
73-
src: url('/fonts/seguiemj.ttf');
73+
src: url('/fonts/seguiemj.woff2') format('woff2');
7474
}
7575

7676
@font-face {

assets/fonts/web/seguiemj.ttf

-2.64 MB
Binary file not shown.

assets/fonts/web/seguiemj.woff2

1.79 MB
Binary file not shown.

0 commit comments

Comments
 (0)