Skip to content

[No QA] Migrate the PR-testing (now AdHoc) Cloudflare Worker to App - #104801

Merged
roryabraham merged 17 commits into
mainfrom
rory/migrate-pr-testing-worker
Nov 12, 2026
Merged

roryabraham merged 17 commits into
mainfrom
rory/migrate-pr-testing-worker

Conversation

@roryabraham

@roryabraham roryabraham commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

Publish new ad-hoc web builds as named Previews under the dedicated new-expensify-adhoc Worker at https://<PR number>.adhoc.expensify.com, with Worker code and web assets built from the same commit.

This uses the shared app Worker runtime and existing deployment token. Builds report success only after verifying that the Preview serves the requested code and assets. There are no separate Workers or routes to manage for each PR.

Note: Cloudflare evicts the least recently deployed Preview when the limit of 500 is reached.

Rollout

The new adhoc.expensify.com domain is configured for Previews. The companion cleanup PR removes deprecated source and deployment configuration from that repository; it can be merged without deleting the live deployment. Keep the existing pr-testing-expensify Worker, *.pr-testing.expensify.com/* route and S3 assets so historical build links continue working. Retiring those live resources is a separate later cleanup.

The Web API CORS change and Nginx/Salt allowlist change are deployed on staging. The automatic post-merge build and web verification passed: the Preview serves the merge commit, and credentialed API/fingerprinting requests succeed without browser CORS errors. Authenticated and SAML sign-in remain unverified.

Merged main and refreshed the local install. All 66 Worker tests, 48 tooling tests, full root/Worker typechecks, branch lint/spelling, formatting and workflow validation pass. The live hosting experiment passed, including isolated builds, cancellation/rebuild, failed-build preservation, closed-PR retention and preservation of an existing S3-backed build. Live bundles were uncompressed; Brotli was not exercised.

Fixed Issues

For https://github.com/Expensify/Expensify/issues/688126
PROPOSAL: N/A

Tests

Steps 1–4 record the earlier pr-testing-expensify experiment; its temporary route removal is not needed for the new setup. Steps 5–10 record the rerun on new-expensify-adhoc / adhoc.expensify.com, with the legacy route left untouched.

  1. Created two disposable PRs against this branch. In PR A, added an X-Preview-Experiment: A-v1 response header in web/cloudflare-worker/src/index.ts and an A-v1 meta tag in web/index.html. In PR B, added the same markers with B-v1. These let us check the deployed Worker code and HTML assets independently. B stayed unchanged as the control throughout the experiment.
  2. Triggered web-only testBuild.yml builds for both PRs using gh workflow run testBuild.yml --repo Expensify/App --ref rory/migrate-pr-testing-worker -f APP_PULL_REQUEST_URL=https://github.com/Expensify/App/pull/105691 -f REVIEWED_CODE=true -f WEB=true -f IOS=false -f ANDROID=false, substituting B's URL for its build. Both published using the existing deployment token. Their final HTTP checks initially failed because the legacy wildcard route still sent requests to the S3 proxy.
  3. In the Cloudflare dashboard: configured the domain for Preview-only hosting and removed the *.pr-testing.expensify.com/* route. Ran gh run rerun 41903167252 --repo Expensify/App --failed and gh run rerun 41903166963 --repo Expensify/App --failed. Confirmed A and B both passed on attempt 2.
  4. Requested / on both hosts (105691.pr-testing.expensify.com and 105692.pr-testing.expensify.com) and checked for HTTP 200, the exact expected Worker response header (A-v1 or B-v1) and the matching HTML meta-tag value.
  5. Merged main, reopened the two disposable fixtures and changed their Worker headers and HTML meta tags to A-adhoc-v1 / B-adhoc-v1. Repeated the web-only dispatch command from step 2. Both A and B published successfully to the corrected new-expensify-adhoc Worker with the updated token. Requested each numeric adhoc.expensify.com host and verified its exact header/meta marker and matching SHA/run/attempt JSON at /__expensify_preview and /preview-build.json.
  6. Ran a local Node HTTPS checker on both hosts. Requested / twice and verified different CSP nonces, each matching the HTML script nonce; checked HTML no-cache and absence of ETag/Last-Modified. Requested /r/12345 as a navigation and verified the marked app shell; requested a nonexistent bundle and /merged-source-map.js.map and verified 404s. Checked an HTML-listed bundle's immutable caching and identical bytes for br and identity requests, and checked that /service-worker.js was not immutable. The bundle responses had no content encoding: ad-hoc builds explicitly skip precompressed Brotli, so this did not test live Brotli compression.
  7. Changed A's header/meta/test expectation to A-adhoc-v2, pushed it, and dispatched twice after the first entered the build workflow. Confirmed the superseded run was canceled and the replacement succeeded. Repeated the HTTP checks: A served its new marker and SHA/run/attempt; B retained its original marker and identity.
  8. Changed A's Wrangler build hook to throw Intentional E2E build failure and dispatched another build. Confirmed that exact error in the failed build and that publication was skipped. Repeated the HTTP checks: A still served its last successful v2 build and B was unchanged. Restored the normal hook in a new pushed commit.
  9. Closed both fixture PRs without merging and repeated the HTTP checks after closure; both Previews remained available with their last-good identities. Also requested the existing legacy build at 106137.pr-testing.expensify.com: its HTML returned 200 and its bundle SHA-256 matched the saved pre-experiment value. No legacy route or Worker was changed.
  10. Opened both new hosts in Playwright and verified /home, their fixture meta tags and the phone/email sign-in forms. Investigated browser CORS errors by requesting staging's /api/Ping?accountID=unknown with matching Origin and Referer headers: the new ad-hoc origin received no CORS headers, while the old PR-testing and staging app origins received the matching Access-Control-Allow-Origin and credentials headers. These API and fingerprinting CORS issues were subsequently fixed on staging and retested successfully; authenticated/SAML sign-in was not performed.

My agent tested a zillion other things, but this was really the core validation ^.

Offline tests

None.

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

Deploy each PR Worker and its matching assets together from one App SHA.
Remove S3 web uploads and keep trusted deployment tooling on main.
Preserve queued push builds while canceling superseded manual builds.
Use named PR Previews under the existing pr-testing-expensify Worker.
Publish branch code and assets without per-PR Workers or zone routes.
Keep artifact validation and code/assets identity checks.
Remove unrelated generated action bundle changes from this migration.
@roryabraham roryabraham changed the title Migrate the PR-testing Cloudflare Worker to App [No QA] Migrate the PR-testing Cloudflare Worker to App Nov 5, 2026
@roryabraham
roryabraham marked this pull request as ready for review November 5, 2026 00:13
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 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-11-05T00:22:05.785664Z 0e9e35b Draft marked ready
ℹ️ 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.

@melvin-bot
melvin-bot Bot requested review from madmax330 and removed request for a team November 5, 2026 00:13
@melvin-bot

melvin-bot Bot commented Nov 5, 2026

Copy link
Copy Markdown

@madmax330 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]

default: 'false'

concurrency:
group: adhoc-${{ github.event_name }}-${{ format('app-{0}-mobile-{1}', inputs.APP_PR_NUMBER || inputs.APP_REF, inputs.MOBILE_EXPENSIFY_PR || inputs.MOBILE_EXPENSIFY_REF) }}

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.

don't we need platform in this key as well?

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.

We could add it but I don't think we need to. I was thinking we only manage concurrency on a per-PR basis, rather than per-platform-per-PR. Basically, if you start a web-only AdHoc build on a PR, then before it finishes start an all-platforms AdHoc build, it's expected that the latter would interrupt the former.

Comment thread package.json
"workspaces": [
"server/victory-chart-renderer"
"server/victory-chart-renderer",
"web/cloudflare-worker"

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.

are you sure about putting this here?

this is going to install all the dev dependencies in every CI run too, right?

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.

I think so - the wrangler dependency is used in scripts which is under the parent project. I think workspaces is the right tool for the job.

That said, you make a good point about unnecessarily bloating npm ci in unrelated CI jobs. So in d5b56e9, I updated our setupNode composite action to accept a workspaces input. Only the workspaces explicitly passed in are installed, and it defaults to an empty array.

@roryabraham
roryabraham requested a review from a team as a code owner November 6, 2026 00:09
@melvin-bot
melvin-bot Bot requested review from arosiclair and removed request for a team November 6, 2026 00:10
@roryabraham
roryabraham dismissed stale reviews from arosiclair and justinpersaud via 9ddb94d November 11, 2026 00:15
@roryabraham
roryabraham requested a review from a team as a code owner November 11, 2026 00:15
@melvin-bot
melvin-bot Bot requested review from heyjennahay and removed request for a team November 11, 2026 00:15
@OSBotify

Copy link
Copy Markdown
Contributor

A preview of your ExpensifyHelp changes have been deployed to https://bb9eef52.helpdot.pages.dev ⚡️

AndrewGable
AndrewGable previously approved these changes Nov 11, 2026
@roryabraham roryabraham changed the title [No QA] Migrate the PR-testing Cloudflare Worker to App [No QA] Migrate the PR-testing (now AdHoc) Cloudflare Worker to App Nov 12, 2026
@roryabraham
roryabraham merged commit 203bddd into main Nov 12, 2026
100 of 107 checks passed
@roryabraham
roryabraham deleted the rory/migrate-pr-testing-worker branch November 12, 2026 00:49
@github-actions

Copy link
Copy Markdown
Contributor

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

@melvin-bot

melvin-bot Bot commented Nov 12, 2026

Copy link
Copy Markdown

@roryabraham looks like this was merged without a test passing. These checks had not passed on 42c0b2b when it was merged:

Please add a note explaining why this was done and remove the Emergency label if this is not an emergency.

@roryabraham

Copy link
Copy Markdown
Contributor Author

didn't look like tests were failing?

@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.

@github-actions

Copy link
Copy Markdown
Contributor

@roryabraham

Copy link
Copy Markdown
Contributor Author

Post-merge verification of the automatic push-to-main build for merge commit 203bddd54c0de0ced95663bf16bdf67fb3deb243.

Web result: passed. The automatic web build and Preview publication both succeeded. The full CI run also finished green; the interactive verification below covers web only.

Tested https://104801.adhoc.expensify.com:

  1. Requested /__expensify_preview and /preview-build.json; both returned the exact merge SHA above, run 43130139625, attempt 1.
  2. Opened the Preview in Playwright; it reached /home and rendered the phone/email sign-in form. Browser fetches with credentials: 'include' read HTTP/JSON 200 from staging's /api/Ping and HTTP 200 JSON from /api/fl/idgib-w-expensify-uat, including the JSON-content-type preflight. No CORS errors remained.
  3. Requested / twice and verified fresh CSP nonces matching the HTML script, HTML no-cache, and no HTML validators. /r/12345 returned the app shell; a nonexistent bundle and /merged-source-map.js.map returned 404.
  4. Verified immutable caching for an HTML-listed bundle and non-immutable caching for /service-worker.js. Requests accepting br and identity returned identical decoded bundle bytes; this build served uncompressed bytes, so live Brotli was not exercised.
  5. Rechecked legacy build 106137.pr-testing.expensify.com: HTML returned 200 and its bundle hash still matched the saved pre-migration baseline.

The only browser console error was the previously observed https://burp/ DNS failure. Authenticated/SAML sign-in and interactive native-app testing were not performed.

@OSBotify

Copy link
Copy Markdown
Contributor

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

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

@MelvinBot

Copy link
Copy Markdown
Contributor

No help site update needed — this PR changes developer ad-hoc build hosting, not customer-facing features or workflows, and makes no existing help article sentence incorrect.


view run

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.

6 participants