Deploy Web #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy Web | |
| # The browser build of the wallet (wallet.fairco.in), on Cloudflare Pages — | |
| # the same host every Oxy frontend uses. It follows CI rather than the push, so | |
| # nothing reaches production that has not typechecked, linted and passed the | |
| # suite first. | |
| on: | |
| workflow_run: | |
| workflows: [CI] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: deploy-fairwallet-web | |
| cancel-in-progress: false | |
| env: | |
| # `workflow_run` reports the commit CI actually verified; a manual dispatch | |
| # runs against the ref it was launched from. | |
| DEPLOY_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} | |
| jobs: | |
| deploy: | |
| # `workflow_run` fires on EVERY conclusion of CI, failures and fork pull | |
| # requests included. Only a green push to this repository's own main may | |
| # reach production. | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_repository.full_name == github.repository && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout the verified commit | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.DEPLOY_SHA }} | |
| - name: Install Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Build the web bundle | |
| run: bun run export:web | |
| - name: Repackage the export for Pages | |
| # Wrangler's Pages upload skips every `node_modules` path, and the SPA | |
| # rewrite answers the skipped assets with index.html, so the loss is | |
| # invisible from outside. `pack:pages` moves that tree and proves every | |
| # referenced asset resolves. | |
| run: bun run pack:pages | |
| - name: Validate the static hosting contract | |
| # `_redirects` and `_headers` are copied out of `public/` by the export. | |
| # If either goes missing the deploy is still green — and every deep link | |
| # 404s, or every visitor keeps a stale bundle. Fail here instead. | |
| run: | | |
| set -euo pipefail | |
| for file in dist/index.html dist/_redirects dist/_headers; do | |
| if [[ ! -s "$file" ]]; then | |
| echo "::error::$file is missing from the export" | |
| exit 1 | |
| fi | |
| done | |
| - name: Deploy to Cloudflare Pages | |
| uses: cloudflare/wrangler-action@v4 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| packageManager: bun | |
| command: pages deploy dist --project-name=fairwallet --branch=main | |
| - name: Smoke test wallet.fairco.in | |
| # Two assertions, because they fail independently: the entry document | |
| # must carry the bundle this build just emitted (proves the promotion | |
| # landed, not that some older deployment answers), and a route with no | |
| # file behind it must return 200 (proves `_redirects` is in force). | |
| run: | | |
| set -euo pipefail | |
| bundle="$(basename dist/_expo/static/js/web/entry-*.js)" | |
| # A dependency asset the bundles reference by its ORIGINAL emitted | |
| # path. It must come back as a font, not as index.html: that is the | |
| # one assertion that fails when the `node_modules` tree goes missing. | |
| # Globbed from the RENAMED tree, requested under the path the | |
| # bundles actually reference — `pack:pages` has already moved | |
| # `assets/node_modules` away by the time this runs. | |
| font_rel="$(cd dist/assets/vendor && echo @expo/vector-icons/build/vendor/react-native-vector-icons/Fonts/MaterialCommunityIcons.*.ttf)" | |
| font="assets/node_modules/$font_rel" | |
| for attempt in $(seq 1 20); do | |
| body="$(curl --fail --silent --show-error https://wallet.fairco.in/ || true)" | |
| deep="$(curl --output /dev/null --silent --write-out '%{http_code}' https://wallet.fairco.in/pockets || true)" | |
| font_type="$(curl --output /dev/null --silent --write-out '%{content_type}' "https://wallet.fairco.in/$font" || true)" | |
| if [[ "$body" == *"$bundle"* && "$deep" == "200" && "$font_type" == font/* ]]; then | |
| echo "wallet.fairco.in serves $bundle, rewrites deep links, and serves $font as $font_type." | |
| exit 0 | |
| fi | |
| if [[ "$attempt" -eq 20 ]]; then | |
| echo "::error::wallet.fairco.in did not converge on $bundle (deep link: $deep, font: $font_type)." | |
| exit 1 | |
| fi | |
| sleep 15 | |
| done |