Repository navigation
[No QA] feat: add native span benchmark tooling - #98144
Conversation
|
|
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
36666c0 to
24d3bc5
Compare
|
@roryabraham the PR is ready for review again, but we'll need to merge this one first, to make Knip work with Bun shell interpolation |
|
#100329 merged |
| debug: 'build-types/debug/res/xml-v25/shortcuts.xml', | ||
| adhoc: 'build-types/adhoc/res/xml-v25/shortcuts.xml', | ||
| } as const; | ||
| for (const buildType of ['release', 'debug', 'adhoc'] as const) { |
There was a problem hiding this comment.
NAB: Do we need to patch all build types?
There was a problem hiding this comment.
I'm changing this, so that by default only release gets patched and other build types can be configured manually
|
didn't see any blockers. Let's merge main to ensure knip passes after the upgrade |
|
@chrispader knip is still failing |
|
@roryabraham sorry forgot to push the main merge. Knip check is succeeding. The PR is now ready for final review and merge! |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 roryabraham has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/roryabraham in version: 9.4.75-0 🚀
|
|
🤖 No help site changes required — no draft PR created. This PR is developer-only tooling. Nothing in it changes behavior a customer can see, so there is nothing to document in What I checkedThe changed files are all internal:
Why none of it belongs on the help site:
Confirmed the help site has no matching content: a case-insensitive search of @chrispader, no help site PR was created because no article changes are needed. If you think a customer-facing behavior changed here that I missed, reply with |
|
🚀 Deployed to production by https://github.com/mountiny in version: 9.4.75-3 🚀
Bundle Size Analysis (Sentry): |
|
🚀 Deployed to production by https://github.com/mountiny in version: 9.4.75-3 🚀
Bundle Size Analysis (Sentry): |
|
@MelvinBot, please create a payment issue for me since I was the C+ reviewer for this PR. |
|
I created the payment issue: [Due for payment 2026-09-17] C+ payment for reviewing https://github.com/Expensify/App/pull/98144. You ( |
Release builds strip ordinary console logging and require different device-control paths on Android and iOS, so startup measurements have depended on one-off commands and results that are difficult to reproduce. Comparing two builds is especially unreliable when they run at different times on a changing device. This PR adds opt-in Sentry span output, repeatable startup benchmarks, and local bootstrap tooling for side-by-side Android and iOS release builds.
@roryabraham @mountiny @tgolen
Explanation of Change
.envwithEXPO_PUBLIC_BENCHMARK_SENTRY_SPANS. Successful, non-canceled spans emit a machine-readable[EXPENSIFY_BENCHMARK]JSON event. iOS also writes one marker per span to the app container because CoreDevice does not expose an equivalent stream of release app logs.benchmark-app-startupruns one warm-up followed by repeated process or true-cold launches. It can collect every configured span or one selected span, use a fixed collection window or stop after a chosen span, warn about incomplete samples, and write both raw samples and Average, P50, P75, P90, P95, P99, Min, and Max results as CSV.resultscommand recalculates statistics from existing sample files. Alternating comparison mode interleaves two installed apps in A/B order and keeps their samples and summaries separate, which reduces bias from device temperature and other conditions that change during a run.bootstrap-deviceprepares uniquely identified local builds that can coexist on one device. The Android adapter updates application IDs, local signing, Firebase clients, shortcuts, manifests, and launcher labels. The iOS adapter updates Xcode targets, signing teams, plist values, app-group entitlements, and launcher labels. Unless an identifier is supplied, the script asksghfor the current user. The GitHub CLI checksGH_TOKEN, thenGITHUB_TOKEN, then its stored active account. Ifghis not installed, the script uses Octokit withGH_TOKEN.tsconfig.jsonextends the repository's Bun configuration so editors load the same Bun globals and module types used by the tooling typecheck. Knip ignores the OpenSSL executable used to inspect provisioning profiles instead of reporting it as an undeclared package binary.JsonValueuntil the required fields pass runtime validation. The script files also document their responsibilities, and non-obvious helpers have JSDoc comments.contributingGuides/BENCHMARKING.mddocuments configuration, side-by-side builds, GitHub username resolution, collection modes, comparisons, result exports, and reusable library entrypoints.Fixed Issues
$ #98280
PROPOSAL: #98280
Tests
.env:gh api user --jq .loginto note the GitHub user selected by the CLI. Runnr bootstrap-device android --suffix candidateornr bootstrap-device ios --suffix candidate, then verify the printed application identifier contains that username. Pass--github-usernameand verify the printed identifier uses the override instead. Build and install the release app fromMobile-Expensify.Expensify (candidate), and that it can be installed beside the app without that suffix.nr benchmark-app-startup ios 20 --device "Developer's iPhone" --wait-time 30--app-idwith the identifier printed by the bootstrap script. On Android, useandroidand the device serial instead. Do not set--wait-until-spanfor this multi-span test, since the network span can finish after startup.runscolumn equals the number of collected samples, and the matching*-results.csvfile contains the displayed aggregate values. The raw sample CSV must contain only observed samples. Any metric with fewer than 20 samples must produce a warning showing collected/requested counts and the missing count. A metric with all 20 samples must not produce that warning.--span ManualAppStartup --wait-until-span ManualAppStartup. Confirm collection ends once startup is observed instead of waiting for the full window. This mode does not promise to collect spans that finish later.BenchmarkNeverEmittedto the allowlist without adding an app span with that name. Rebuild and reinstall, then run two measured launches with--wait-time 1and neither--spannor--wait-until-span. Confirm the warning includesBenchmarkNeverEmitted: 0/2 samples collected; 2 missing, its console and results CSV rows showruns=0andN/Astatistics, and the raw sample CSV contains no rows for it. Restore the original allowlist and rebuild afterward..benchmarks/results.csv. Repeat without--results-outputand verify it writes.benchmarks/results.csvby default.resultscommand with an invalid sample header and verify it exits with a clear error. Repeat a live benchmark with a--spanvalue absent from the allowlist and verify it exits without launching the app.--app-id-aand--app-id-b, then verify the terminal alternates A and B for every cycle and produces separate raw and results CSV files for both binaries. Repeat the missing-span test and confirm each warning identifies binary A or B and uses that binary's sample count.[EXPENSIFY_BENCHMARK]output and incomplete-sample warnings from the deliberate missing-span test are expected.Offline tests
Offline testing was not performed. The change adds local release-build logging and device automation without changing network behavior. Running a benchmark offline would measure the existing offline behavior of the selected span, not behavior introduced by this PR.
QA Steps
This PR cannot be tested in a standard staging or production build. The span allowlist, app identifiers, and signing changes are supplied while producing a local release build, and QA does not have access to that build workflow or the required command-line tooling.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
No screenshots or videos are included because the benchmark has no in-app UI. The iOS launcher-name suffix was verified locally as part of the developer setup flow.