diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5fb82501a3d4..703f28d925d1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -703,74 +703,6 @@ jobs: release-tag: ${{ needs.prep.outputs.TAG }} secrets: inherit - webDeploy: - name: Deploy Web to S3 - needs: [prep, webBuild, buildStorybook] - if: ${{ always() && needs.webBuild.result == 'success' }} - # Preserve the default token scopes while adding OIDC access - permissions: write-all - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout - uses: useblacksmith/checkout@0647fdbab2614a5eb86d2971070e325060d91056 # v1.7.0 - with: - ref: ${{ needs.prep.outputs.DEPLOY_SHA }} - - - 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 -xzvf "${{ needs.webBuild.outputs.TAR_FILENAME }}" - - - name: Download storybook docs artifact - continue-on-error: true - # v7 - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 - with: - name: storybook-docs-artifact - path: ./dist/docs - - - name: Setup Cloudflare CLI - run: pip3 install cloudflare==2.19.0 - - - name: Configure AWS Credentials - # v6 - uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 - with: - role-to-assume: arn:aws:iam::949552126497:role/app-${{ env.DEPLOY_ENV }}-web-deploy-gh-actions - role-session-name: gh-${{ github.run_id }}-${{ github.run_attempt }} - aws-region: us-east-1 - - - name: Deploy to S3 - run: | - aws s3 cp --recursive --acl public-read --exclude "*.br" "$GITHUB_WORKSPACE"/dist ${{ env.S3_BUCKET }}/ - - for PAIR in js:text/javascript css:text/css html:text/html svg:image/svg+xml wasm:application/wasm ttf:font/ttf; do - EXT="${PAIR%%:*}" - CONTENT_TYPE="${PAIR#*:}" - aws s3 cp --recursive --acl public-read --exclude "*" --include "*.${EXT}.br" \ - --content-encoding br --content-type "$CONTENT_TYPE" \ - "$GITHUB_WORKSPACE"/dist ${{ env.S3_BUCKET }}/ - done - - aws s3 cp --acl public-read --content-type 'application/json' --metadata-directive REPLACE ${{ env.S3_BUCKET }}/.well-known/apple-app-site-association ${{ env.S3_BUCKET }}/.well-known/apple-app-site-association - aws s3 cp --acl public-read --content-type 'application/json' --metadata-directive REPLACE ${{ env.S3_BUCKET }}/.well-known/apple-app-site-association ${{ env.S3_BUCKET }}/apple-app-site-association - env: - S3_BUCKET: s3://${{ env.DEPLOY_ENV == 'staging' && 'staging-' || '' }}${{ vars.PRODUCTION_S3_BUCKET }} - - - name: Purge Cloudflare cache - run: | - /home/runner/.local/bin/cli4 --verbose --delete hosts=["$HOST"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache - env: - CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - HOST: ${{ env.DEPLOY_ENV == 'production' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }} - - # The host is verified by webDeployWorker, which deploys after this job. - webDeployTestStaging: name: Deploy Web to test-staging.new.expensify.com needs: [prep, webBuild, buildStorybook] @@ -829,10 +761,9 @@ jobs: env: HOST: https://test-staging.new.expensify.com - webDeployWorker: + webDeploy: name: Deploy Web to the 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] + needs: [prep, webBuild, buildStorybook] if: ${{ always() && needs.webBuild.result == 'success' }} runs-on: blacksmith-2vcpu-ubuntu-2404 steps: @@ -953,7 +884,6 @@ jobs: iosSubmit, webBuild, webDeploy, - webDeployWorker, victoryChartRendererBuild, ] steps: @@ -969,7 +899,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, webDeployWorker] + needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy] steps: - name: Check whether a newer deploy run exists id: check @@ -1008,7 +938,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, webDeployWorker, victoryChartRendererBuild] + needs: [androidBuild, androidUploadGooglePlay, androidSubmit, iosBuild, iosUploadTestflight, iosSubmit, webBuild, webDeploy, victoryChartRendererBuild] if: ${{ always() }} steps: # Determine effective result for each platform @@ -1043,11 +973,9 @@ jobs: echo "IOS_RESULT=${{ needs.iosUploadTestflight.result }}" >> "$GITHUB_OUTPUT" fi - # Web: propagate build failure even when deploy is skipped. Web is only deployed once its Worker is too. + # Web: propagate build failure even when deploy is skipped. if [ "${{ needs.webBuild.result }}" == "failure" ]; then echo "WEB_RESULT=failure" >> "$GITHUB_OUTPUT" - elif [ "${{ needs.webDeployWorker.result }}" != "success" ]; then - echo "WEB_RESULT=${{ needs.webDeployWorker.result }}" >> "$GITHUB_OUTPUT" else echo "WEB_RESULT=${{ needs.webDeploy.result }}" >> "$GITHUB_OUTPUT" fi diff --git a/tests/tooling/verifyStagingBuildsSucceeded.test.ts b/tests/tooling/verifyStagingBuildsSucceeded.test.ts index 77306e79e9d4..9187c89a443d 100644 --- a/tests/tooling/verifyStagingBuildsSucceeded.test.ts +++ b/tests/tooling/verifyStagingBuildsSucceeded.test.ts @@ -41,7 +41,7 @@ function stagingDeployJobs(overrides: Array<[string, JobConclusion | null]> = [] [IOS_BUILD, 'success'], [IOS_UPLOAD, 'success'], ['Build Web / Build Web', 'success'], - ['Deploy Web to S3', 'success'], + ['Deploy Web to the Cloudflare Worker', 'success'], // Production-only, skipped on every staging deploy. ['Submit Android for production rollout', 'skipped'], @@ -188,7 +188,7 @@ describe('verifyStagingBuildsSucceeded', () => { test('ignores a failed web deploy and the continue-on-error uploads', async () => { mockListJobs.mockResolvedValue( stagingDeployJobs([ - ['Deploy Web to S3', 'failure'], + ['Deploy Web to the Cloudflare Worker', 'failure'], ['Upload iOS to BrowserStack', 'failure'], ['Upload Android to Applause', 'failure'], ]), diff --git a/web/cloudflare-worker/README.md b/web/cloudflare-worker/README.md index 34887a303e78..c3d15533acaf 100644 --- a/web/cloudflare-worker/README.md +++ b/web/cloudflare-worker/README.md @@ -113,9 +113,9 @@ The script diffs status, caching and security headers for a fixed set of paths. | `staging` | `staging-new-expensify-assets` | `staging.new.expensify.com/*` (managed in Cloudflare, not in `wrangler.jsonc`) | Staging | | `production` | `new-expensify-assets` | None until switchover, then `new.expensify.com/*` (managed in Cloudflare, not in `wrangler.jsonc`) | Production | -Every staging and production deploy also deploys the same build to that environment's Worker (the `webDeployWorker` job in `.github/workflows/deploy.yml`). Staging deploys also deploy it to the `test-staging` Worker (`webDeployTestStaging`). They use `CLOUDFLARE_WORKERS_DEPLOY_TOKEN`, which can only edit these three Workers. That token can't read zones, so routes aren't in `wrangler.jsonc`: wrangler would try to sync them and fail. +Every staging and production deploy also deploys the same build to that environment's Worker (the `webDeploy` job in `.github/workflows/deploy.yml`). Staging deploys also deploy it to the `test-staging` Worker (`webDeployTestStaging`). They use `CLOUDFLARE_WORKERS_DEPLOY_TOKEN`, which can only edit these three Workers. That token can't read zones, so routes aren't in `wrangler.jsonc`: wrangler would try to sync them and fail. -The Worker deploy runs after `webDeploy` and is required: if it fails, or `WEB_STAGING_HOST` or `WEB_PRODUCTION_HOST` doesn't report the new version in `/version.json`, the deploy fails. The version is checked there because both jobs have deployed by then, whichever one serves the host. The `test-staging` deploy is a trial, so its failures don't affect the staging deploy. +The Worker deploy is required: if it fails, or `WEB_STAGING_HOST` or `WEB_PRODUCTION_HOST` doesn't report the new version in `/version.json`, the deploy fails. A new Worker version takes effect immediately, so no cache needs purging. 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.