Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 73 additions & 17 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -769,24 +769,21 @@ jobs:
CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }}
HOST: ${{ env.DEPLOY_ENV == 'production' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }}

# The staging host is verified by webDeployStagingWorker, which deploys after this job.
- name: Verify deploy
if: ${{ env.DEPLOY_ENV == 'production' }}
run: |
APP_VERSION=$(jq -r .version < package.json)
./.github/scripts/verifyDeploy.sh "$HOST" "$APP_VERSION"
env:
HOST: ${{ env.DEPLOY_ENV == 'production' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }}
HOST: ${{ vars.WEB_PRODUCTION_HOST }}

webDeployCloudflareWorkers:
name: Deploy Web to Cloudflare Workers (${{ matrix.environment }})
webDeployTestStaging:
name: Deploy Web to test-staging.new.expensify.com
needs: [prep, webBuild, buildStorybook]
if: ${{ always() && needs.webBuild.result == 'success' && needs.prep.outputs.DEPLOY_ENV == 'staging' }}
# Trial hosting for the web build, so a failure here must not fail the staging deploy.
continue-on-error: true
strategy:
fail-fast: false
matrix:
# `staging` serves no traffic until staging.new.expensify.com is routed to its Worker in Cloudflare.
environment: [test-staging, staging]
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout
Expand All @@ -812,10 +809,65 @@ jobs:
- name: Extract web build
run: tar -xzf "${{ needs.webBuild.outputs.TAR_FILENAME }}"

# The Workers serve these to tabs still running this build after later deploys. Both Workers read the same bucket,
# so only one matrix leg uploads.
- name: Download storybook docs artifact
continue-on-error: true
# v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
with:
name: storybook-docs-artifact
path: ./dist/docs

- name: Install Cloudflare Worker dependencies
working-directory: web/cloudflare-worker
run: npm ci

- name: Deploy to Cloudflare Workers
working-directory: web/cloudflare-worker
run: npx wrangler deploy --env test-staging
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_WORKERS_DEPLOY_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WRANGLER_SEND_METRICS: false

- name: Verify deploy
run: |
APP_VERSION=$(jq -r .version < package.json)
./.github/scripts/verifyDeploy.sh "$HOST" "$APP_VERSION"
env:
HOST: https://test-staging.new.expensify.com

webDeployStagingWorker:
name: Deploy Web to the staging Cloudflare Worker
# After webDeploy, so the S3 copy of this build is in place before the Worker's version check.
needs: [prep, webBuild, buildStorybook, webDeploy]
if: ${{ always() && needs.webBuild.result == 'success' && needs.prep.outputs.DEPLOY_ENV == 'staging' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout
uses: useblacksmith/checkout@0647fdbab2614a5eb86d2971070e325060d91056 # v1.7.0
with:
ref: ${{ needs.prep.outputs.DEPLOY_SHA }}

- name: Setup Node
# v6.1.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f
with:
node-version-file: '.nvmrc'
cache: npm
cache-dependency-path: web/cloudflare-worker/package-lock.json

- name: Download web build artifact
# v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
with:
name: web-build-tar-gz-artifact
path: ./

- name: Extract web build
run: tar -xzf "${{ needs.webBuild.outputs.TAR_FILENAME }}"

# The Workers serve these to tabs still running this build after later deploys.
- name: Upload web build to the previous builds bucket
if: ${{ matrix.environment == 'staging' }}
continue-on-error: true
run: |
if [[ -z "$AWS_ACCESS_KEY_ID" ]]; then
Expand Down Expand Up @@ -848,17 +900,18 @@ jobs:

- name: Deploy to Cloudflare Workers
working-directory: web/cloudflare-worker
run: npx wrangler deploy --env "${{ matrix.environment }}"
run: npx wrangler deploy --env staging
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_WORKERS_DEPLOY_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WRANGLER_SEND_METRICS: false

- name: Verify deploy
if: ${{ matrix.environment == 'test-staging' }}
run: |
APP_VERSION=$(jq -r .version < package.json)
./.github/scripts/verifyDeploy.sh https://test-staging.new.expensify.com "$APP_VERSION"
./.github/scripts/verifyDeploy.sh "$HOST" "$APP_VERSION"
env:
HOST: ${{ vars.WEB_STAGING_HOST }}

buildStorybook:
name: Build storybook docs
Expand Down Expand Up @@ -907,6 +960,7 @@ jobs:
iosSubmit,
webBuild,
webDeploy,
webDeployStagingWorker,
victoryChartRendererBuild,
]
steps:
Expand All @@ -922,7 +976,7 @@ jobs:
name: Post a Slack message when the deploy is cancelled manually
runs-on: blacksmith-2vcpu-ubuntu-2404
if: ${{ cancelled() }}
needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy]
needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy, webDeployStagingWorker]
steps:
- name: Check whether a newer deploy run exists
id: check
Expand Down Expand Up @@ -961,7 +1015,7 @@ jobs:
ANDROID_RESULT: ${{ steps.platformResults.outputs.ANDROID_RESULT }}
IOS_RESULT: ${{ steps.platformResults.outputs.IOS_RESULT }}
WEB_RESULT: ${{ steps.platformResults.outputs.WEB_RESULT }}
needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy, victoryChartRendererBuild]
needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy, webDeployStagingWorker, victoryChartRendererBuild]
if: ${{ always() }}
steps:
# Determine effective result for each platform
Expand Down Expand Up @@ -996,9 +1050,11 @@ jobs:
echo "IOS_RESULT=${{ needs.iosUploadTestflight.result }}" >> "$GITHUB_OUTPUT"
fi

# Web: propagate build failure even when deploy is skipped
# Web: propagate build failure even when deploy is skipped. Staging is only deployed once its Worker is too.
if [ "${{ needs.webBuild.result }}" == "failure" ]; then
echo "WEB_RESULT=failure" >> "$GITHUB_OUTPUT"
elif [ "${{ env.DEPLOY_ENV }}" == "staging" ] && [ "${{ needs.webDeployStagingWorker.result }}" != "success" ]; then
echo "WEB_RESULT=${{ needs.webDeployStagingWorker.result }}" >> "$GITHUB_OUTPUT"
else
echo "WEB_RESULT=${{ needs.webDeploy.result }}" >> "$GITHUB_OUTPUT"
fi
Expand Down
4 changes: 3 additions & 1 deletion web/cloudflare-worker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ The script diffs status, caching and security headers for a fixed set of paths.
| `staging` | `staging-new-expensify-assets` | None until switchover, then `staging.new.expensify.com/*` (managed in Cloudflare, not in `wrangler.jsonc`) | Staging |
| `production` | `new-expensify-assets` | None until switchover, then `new.expensify.com/*` | Production |

Every staging deploy also deploys the same build to the `test-staging` and `staging` Workers (the `webDeployCloudflareWorkers` job in `.github/workflows/deploy.yml`). It uses `CLOUDFLARE_WORKERS_DEPLOY_TOKEN`, which can only edit those two Workers. That token can't read zones, so routes aren't in `wrangler.jsonc`: wrangler would try to sync them and fail. A failure there does not fail the staging deploy.
Every staging deploy also deploys the same build to the `staging` Worker (the `webDeployStagingWorker` job in `.github/workflows/deploy.yml`) and the `test-staging` Worker (`webDeployTestStaging`). Both use `CLOUDFLARE_WORKERS_DEPLOY_TOKEN`, which can only edit those two Workers. That token can't read zones, so routes aren't in `wrangler.jsonc`: wrangler would try to sync them and fail.

The `staging` deploy runs after the S3 upload and is required: if it fails, or `staging.new.expensify.com/version.json` doesn't report the new version, the staging deploy fails. The `test-staging` deploy is a trial, so its failures don't affect the staging deploy.

`test-staging` serves a staging build on a hostname nobody else uses. `staging` serves nothing until `staging.new.expensify.com/*` is pointed at its Worker in Cloudflare.

Expand Down
Loading