Skip to content

[No QA] Run the Cloudflare Worker during local web development - #104525

Merged
roryabraham merged 38 commits into
mainfrom
plan/104387-local-cloudflare-worker
Oct 26, 2026
Merged

roryabraham merged 38 commits into
mainfrom
plan/104387-local-cloudflare-worker

Conversation

@roryabraham

@roryabraham roryabraham commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

Run the real NewDot Worker in local workerd during npm run web so CSP, nonce, security-header, and routing regressions are caught before staging.

  • Install the independently locked Worker package during normal App setup.
  • Serve trusted HTTPS at the Worker and forward live app requests and HMR WebSockets to loopback HTTP Rsbuild. No ngrok, dist/, or Cloudflare credentials are needed.
  • Preserve both React Fast Refresh and Wrangler reload of the entry point/imported CSP modules.
  • Add independent Worker/API-proxy opt-outs, coordinated ports, shell-over-.env settings, public-origin readiness, browser opening, and process cleanup.
  • Use Bun-native file I/O, shell commands/escaping, environment loading, sleep, socket listeners, and TLS-aware fetch in the Bun-owned launcher/helpers. Keep Wrangler/Rsbuild on Node and preserve concurrently lifecycle management.
  • Keep built-site preview/deploy settings separate. Staging and production CSP are byte-for-byte unchanged.

Fixed Issues

$ #104387

Tests

  1. Run npm i && npm run web
  2. Perform some basic smoke tests of the app: sign out and back in, click around, create expenses, load insights, etc...
  • Verify that no errors appear in the JS console

Offline tests

n/a

QA Steps

None.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified there are no new alerts related to the canBeMissing param for useOnyx
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

Catch CSP and security-header regressions before staging while preserving React Fast Refresh and Wrangler source reload. Use trusted local HTTPS and loopback HTTP instead of requiring ngrok.
@roryabraham
roryabraham requested a review from a team October 21, 2026 01:59
@melvin-bot

melvin-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown

npm has a package.json file and a package-lock.json file. It seems you updated one without the other, which is usually a sign of a mistake. If you are updating a package make sure that you update the version in package.json then run npm install

@roryabraham
roryabraham marked this pull request as ready for review October 22, 2026 21:41
@melvin-bot
melvin-bot Bot requested review from abdulrahuman5196 and removed request for a team October 22, 2026 21:41
@melvin-bot

melvin-bot Bot commented Oct 22, 2026

Copy link
Copy Markdown

@abdulrahuman5196 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@roryabraham roryabraham changed the title Run the Cloudflare Worker during local web development [No QA] Run the Cloudflare Worker during local web development Oct 22, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T07:18:04.176938Z — Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

arosiclair
arosiclair previously approved these changes Oct 23, 2026

@arosiclair arosiclair left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and tests well for me 👍

justinpersaud
justinpersaud previously approved these changes Oct 23, 2026
@AndrewGable

Copy link
Copy Markdown
Contributor

@roryabraham fyi conflicts

@roryabraham
roryabraham dismissed stale reviews from justinpersaud and arosiclair via a9999d9 October 23, 2026 20:05
@roryabraham
roryabraham requested a review from a team as a code owner October 23, 2026 20:05
Node 26 types declare tls.getCACertificates, so the local module augmentation is no longer needed. Their stricter exec and webcrypto overloads needed an explicit encoding type in Git.ts, and Node buffer types in the CloudflareAccess test mocks.
@situchan

Copy link
Copy Markdown
Contributor

@MelvinBot review

@situchan

Copy link
Copy Markdown
Contributor

@codex review

@situchan

situchan commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified any copy / text that was added to the app is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If the PR adds or modifies the UI:
    • I asked an AI agent to review the changes for accessibility issues and addressed its findings.
    • I tested with a screen reader (VoiceOver on macOS) and verified all new/changed elements are reachable with a logical focus order.
    • I verified all new/changed elements have meaningful accessible names and roles.
    • I verified state changes are announced (e.g. checked/unchecked, expanded/collapsed, selected).
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-10-24.at.9.06.34.AM.mov
Screen.Recording.2026-10-24.at.9.07.34.AM.mov

Comment thread scripts/startWeb.ts Outdated
});
}

export default getWebCommands;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startWeb.ts default-exports getWebCommands

The file is named for the launcher but exports a different function so the test can import it. That breaks the checklist rule that files are named after what they export.

Fix: Move getWebCommands to scripts/utils/getWebCommands.ts, or use a named export.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1d87d41

@MelvinBot

Copy link
Copy Markdown
Contributor

Review

I found no blocking issues. Your earlier review comments are all fixed on 222c691: shared API paths and port, shared backend keys, a single readiness loop, 'self'/backend origins in dev media-src/frame-src, no mkcert at runtime, and the Worker install skipped in CI and cached by lockfile hash. Staging and production CSP output is unchanged, because the new connectSrcHosts/mediaSrcHosts default to [].

I have two suggestions:

  1. A failed Worker install now fails every root npm install. scripts/postInstall.sh:44 runs under set -e. If npm ci in web/cloudflare-worker fails (registry hiccup, workerd download, offline), install aborts for iOS and Android developers too, even though they never use the Worker. startWeb.ts:83-85 already detects missing Wrangler and tells the user to rerun npm install. You could make this step warn instead of fail, for example bun scripts/installCloudflareWorker.ts || echo "Worker tooling install failed; npm run web will need it".
  2. The Worker test workflow now runs on unrelated changes. cloudflareWorkerTests.yml:9-16 adds package.json, config/rsbuild/**, and the launcher scripts. That job only typechecks and tests web/cloudflare-worker, and nothing there imports those files. The launcher's own tests live in tests/tooling/ and run under bun test. As written, every root dependency bump runs this job without testing the change. Consider dropping those paths.

I could not test this in a browser because the web dev server failed to start in my environment. This review comes from reading the code only.

Comment thread config/rsbuild/developmentEnvKeys.ts Outdated
@@ -0,0 +1,7 @@
/** Local backends from `.env`, which the app calls and the launcher adds to the live Worker's CSP. */
const LOCAL_BACKEND_URL_KEYS = ['EXPENSIFY_URL', 'SECURE_EXPENSIFY_URL', 'STAGING_EXPENSIFY_URL', 'STAGING_SECURE_EXPENSIFY_URL', 'NGROK_URL', 'SECURE_NGROK_URL'];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dev CSP blocks the QA server and its Cloudflare Access sign-in

With QA configured in .env, the app calls QA_EXPENSIFY_URL/QA_SECURE_EXPENSIFY_URL directly with no proxy, and the Access flow fetches metadata from the QA host and tokens from https://<QA_CF_TEAM_DOMAIN>. LOCAL_BACKEND_URL_KEYS has no QA keys and the base CSP has no QA or Access host, so the dev connect-src blocks all of them. On main the dev server sends no CSP, so they work.

  • const LOCAL_BACKEND_URL_KEYS = ['EXPENSIFY_URL', 'SECURE_EXPENSIFY_URL', 'STAGING_EXPENSIFY_URL', 'STAGING_SECURE_EXPENSIFY_URL', 'NGROK_URL', 'SECURE_NGROK_URL'];
  • connectSrcHosts: [...backendOrigins, ...(developmentOrigins ? [developmentOrigins.hmrOrigin] : [])],
  • App/src/libs/ApiUtils.ts

    Lines 95 to 108 in 222c691

    const server = forceProduction ? CONST.SERVER.PRODUCTION : getCurrentActiveServerState().activeServer;
    if (server === CONST.SERVER.QA) {
    // No web-proxy branch: Cloudflare Access answers the preflight and matches the bearer against the
    // real origin, so routing QA through a same-origin proxy path would defeat both
    if (!shouldUseSecure) {
    return CONFIG.EXPENSIFY.QA_API_ROOT;
    }
    if (!CONFIG.EXPENSIFY.QA_SECURE_API_ROOT) {
    throw new Error(`The QA server has no secure host, so it cannot serve ${request?.command ?? 'a secure command'}. Set QA_SECURE_EXPENSIFY_URL to reach one.`);
    }
    return CONFIG.EXPENSIFY.QA_SECURE_API_ROOT;
  • const response = await fetch(new URL(WELL_KNOWN_PATH, getQAResource()).href, {
  1. Set QA_EXPENSIFY_URL, QA_SECURE_EXPENSIFY_URL, QA_CF_TEAM_DOMAIN, QA_CF_OAUTH_CLIENT_ID and QA_AUTH_CHECK_PATH in .env
  2. Run npm run web with the Worker on (default)
  3. Go to Settings → Troubleshoot
  4. In the QA auth (Cloudflare) row, click Run probe

Expected: The Cloudflare sign-in starts or the probe succeeds, as on main.
Actual: The probe fails. Console shows connect-src CSP violations for the QA host.

Fix: Add the QA URL keys to LOCAL_BACKEND_URL_KEYS and allow https://${QA_CF_TEAM_DOMAIN} in dev connect-src.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2782299

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@MelvinBot

Copy link
Copy Markdown
Contributor

Regression check

I found one likely regression and one behavior change. Staging and production are not affected.

  1. npm run web fails anywhere CI is set. installCloudflareWorker.ts:16-18 skips the Worker install when CI is set. But the Worker is on by default (webDevConfig.ts:5), so startWeb.ts:74-76 throws "Missing Worker dependencies." This hits cloud agents, containers, and CI-run browser test harnesses that run npm ci && npm run web. That worked on main. It is likely why my own web dev server failed to start on this PR. Fix: when Wrangler is missing, warn and fall back to direct Rsbuild instead of throwing.

  2. A second npm run web session no longer starts. On main, Rsbuild used portfinder, so a second worktree moved to port 8083 and shared the first session's proxy on port 9000. Now startWeb.ts:63-68 exits if port 8082, 8083, or 9000 is busy. USE_WEB_PROXY=false does not restore the old setup, because the app then calls the API directly and hits CORS errors. SETUP_WEB.md documents this, so it may be intended.

What I checked with no regression found
  • Deployed Worker: every dev branch is gated on ENVIRONMENT === 'development', which only wrangler.dev.jsonc sets. The new connectSrcHosts/mediaSrcHosts default to [], so staging and production CSP output is unchanged. Deploy still runs wrangler deploy --env …, and the only new dependencies (ws, @types/ws) are dev-only.
  • Dev CSP and nonce: the HMR wss:// origin is allowed, and 'unsafe-eval' and blob: are still present. Style and preload tags that keep the placeholder nonce are covered by 'unsafe-inline' and 'self'.
  • web-server, Storybook, and production/staging/native builds: without WEB_LAUNCHER, getDevServerSettings.ts keeps the old port, host, and HTTPS behavior. The non-dev branch of rsbuild.config.ts is unchanged.
  • web/proxy.ts: only the literal 9000 became API_PROXY_PORT.

Low-risk items I could not verify:

  • The browser now opens only after checkWebServer trusts the certificate through Bun's system CA store. If mkcert's CA is only in the browser's store (some Linux and WSL setups), the browser never opens. You get a warning after 3 minutes.
  • postInstall.sh:44 runs under set -e. An offline machine or a failed workerd download now fails every root npm install.

I could not verify this in a browser because the web dev server failed to start in my environment. These findings come from reading the code.

@MelvinBot

Copy link
Copy Markdown
Contributor

Confirmed: the dev CSP blocks QA. I checked the code at 222c691, but I couldn't check it in a browser.

Fix (in scripts/utils/webDevConfig.ts and config/rsbuild/developmentEnvKeys.ts):

  1. Add QA_EXPENSIFY_URL and QA_SECURE_EXPENSIFY_URL to LOCAL_BACKEND_URL_KEYS.
  2. In getDevelopmentConnectOrigins, add https://${env.QA_CF_TEAM_DOMAIN} as a separate step. Don't add QA_CF_TEAM_DOMAIN to LOCAL_BACKEND_URL_KEYS. It is a bare hostname, so new URL(value) would throw and stop npm run web.
Evidence

⚠️ I can't push changes to this PR because I didn't open it and I'm not listed as an assignee. To have me make this fix, add me to the PR assignees and then retry your request.


view run

startWeb.ts default-exported a different function so a test could import it, which breaks the rule that files are named after what they export.
…v CSP

With QA configured, the app calls the QA hosts directly and the Access flow calls https://$QA_CF_TEAM_DOMAIN, but dev connect-src allowed neither, so the QA auth probe and sign-in failed. Add the QA URLs to the backend keys and allow the team domain, which must be a bare hostname.
npm install skips the Worker when CI is set, so npm run web failed in containers, cloud agents, and CI-run browser tests that worked on main. Warn and fall back to direct Rsbuild instead.
… fails

postInstall runs under set -e, so an offline machine or a failed workerd download failed every App install, including for developers who never run the Worker. npm run web now serves Rsbuild directly without it, and the next install retries.
The job only typechecks and tests web/cloudflare-worker, which reads no launcher, Rsbuild, or root package files. Those have their own tests under tests/tooling.
On main, a second worktree's npm run web moved to the next free port and shared the first session's proxy on 9000. The launcher instead failed if 8082, 8083, or 9000 was busy, and nothing could get a second proxy-enabled session past 9000. Pick the first free ports unless WEB_PORT or WEB_INTERNAL_PORT is set, and reuse a running proxy.
@roryabraham

Copy link
Copy Markdown
Contributor Author

@AndrewGable

Copy link
Copy Markdown
Contributor

@roryabraham conflicts

@@ -0,0 +1,28 @@
/** Local backends from `.env`, which the app calls and the launcher adds to the live Worker's CSP. */
const LOCAL_BACKEND_URL_KEYS = [

@AndrewGable AndrewGable Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nab Seems kind of annoying to maintain, is there a better way to do this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I have a better suggestion - fortunately it just references keys in a .env, which makes it easy to configure per-workspace (such as if you're trying to set up parallel Docker stacks).

@roryabraham
roryabraham merged commit f878170 into main Oct 26, 2026
54 of 55 checks passed
@roryabraham
roryabraham deleted the plan/104387-local-cloudflare-worker branch October 26, 2026 20:19
@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/roryabraham in version: 9.5.21-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@github-actions

Copy link
Copy Markdown
Contributor

🚧 roryabraham has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

Copy link
Copy Markdown
Contributor

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/roryabraham in version: 9.5.23-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants