Skip to content

Commit

Permalink
Deploy staging to Cloudflare previews (#3055)
Browse files Browse the repository at this point in the history
This adds a script to manually deploy to Cloudflare previews, a production-like environment where we can deploy a copy of the baked site for testing.

This is particularly useful for testing CF Pages functions and increase confidence, in addition to the local/staging testing done with `wrangler`.

This requires quite a few manual steps to set up, which is fine for now since the scope of these functions is still pretty limited. As usage ramps up, we can consider a more automated approach to deploying these functions to CF, parallel to the current staging deploys.

For testing the donation flow:
- https://donate.owid.pages.dev
- temporarily disable CF Access rule, see https://github.com/owid/owid-grapher/blob/74c74736a6ccb5863c4693762a69a1726fe50563/functions/README.md?plain=1#L156-L160
  • Loading branch information
mlbrgl authored Jan 11, 2024
2 parents 1136447 + a0a3c63 commit efe4bd7
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 0 deletions.
25 changes: 25 additions & 0 deletions functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ Note: compatibility dates between local development, production and preview envi

3. _Refer to each function's "Development" section below for further instructions._

## Testing on Fondation staging sites vs Cloudfare previews

`yarn deployContentPreview` deploys the staging `bakedSite` to a Cloudflare preview at https://[PREVIEW_BRANCH].owid.pages.dev. This is the recommended way to test functions in a production-like environment. See [../ops/buildkite/deploy-content-preview](../ops/buildkite/deploy-content-preview) for more details.

### Rationale

A custom staging site is available at http://staging-site-[BRANCH] upon pushing your branch (see ops > templates > lxc-manager > staging-create). This site is served by `wrangler` (see ops > templates > owid-site-staging > grapher-refresh.sh). `wrangler` is helpful for testing the functions locally (and possibly for some debugging scenarios on staging servers), but is still not the closest match to the production Cloudflare environment.

When it comes to testing functions in a production-like environment, Cloudlfare previews are recommended.

Cloudflare previews are served by Cloudflare (as opposed to `wrangler` on staging sites) and are available at https://[RANDOM_ID].owid.pages.dev. Cloudflare previews do not rely on the `wrangler` CLI and its `.dev.vars` file. Instead, they use the [Cloudflare dashboard to configure environment variables](https://dash.cloudflare.com/078fcdfed9955087315dd86792e71a7e/pages/view/owid/settings/environment-variables), in the same way and place as the production site.

This proximity of configurations in the Cloudflare dashboard makes spotting differences between production and preview environments easier - and is one of the reason of using Cloudflare previews in the same project (owid) over using a new project specific to staging.

Our workflow uses `wrangler pages deploy` to deploy the `bakedSite` to
Cloudflare. Similarly, `yarn deployContentPreview` uses `wrangler pages deploy
--branch=[PREVIEW_BRANCH]` to deploy the `bakedSite` to a Cloudflare preview at
https://[PREVIEW_BRANCH].owid.pages.dev.

# Our dynamic routes

## `/donation/donate`
Expand Down Expand Up @@ -134,6 +153,12 @@ stripe trigger checkout.session.completed

Note: this will send the `checkout.session.completed` event expected in `/donation/thank-you`. However, I didn't manage to add metadata with `--add checkout.session:metadata.name="John Doe" --add checkout.session:metadata.showOnList=true` to perform a full test.

### Testing on Cloudflare previews

The webhook registered in the Stripe dashboard is configured to send test events to https://donate.owid.pages.dev/donation/thank-you.

There is however a [Cloudflare Access rule restricting access to \*.owid.pages.dev](https://one.dash.cloudflare.com/078fcdfed9955087315dd86792e71a7e/access/apps/edit/d8c658c3-fd20-477e-ac20-e7ed7fd656de?tab=overview), so the Stripe webhook invocation will hit the Google Auth page instead of the function. To test the webhook on a Cloudflare preview, you need to temporarily disable the Cloudflare Access rule (the easiest is to change the rule to an unused subdomain, e.g. `temp.owid.pages.dev`).

## `/grapher/:slug`

Our grapher pages are (slightly) dynamic!
Expand Down
170 changes: 170 additions & 0 deletions ops/buildkite/deploy-content-preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/bin/bash
#
# deploy-content-preview
#
# Manually deploy the staging bakedSite to Cloudflare Pages in preview mode
#
# See /functions/README.md > "Testing on Fondation staging sites vs Cloudfare
# previews" for the rationale behind this script.
#
# USAGE
#
# - $ yarn deploy-content-preview (default, deploys to
# https://[YOUR_CURRENT_GIT_BRANCH].owid.pages.dev)
# - $ PREVIEW_BRANCH=[PREVIEW_BRANCH] yarn deploy-content-preview (deploys to
# https://[PREVIEW_BRANCH].owid.pages.dev)
#
# This script is meant to be run manually and locally. It is not triggered
# automatically and run by an Buildkite agent, unlike its production
# counterpart `deploy-content`. Instead the script SSH into to the Foundation
# staging server and runs the deployment commands from there.
#
# By default, running `yarn deploy-content-preview` will deploy to a preview
# URL prefixed with the current git branch name. For example, if you're on the
# `donate` branch, the preview URL will be https://donate.owid.pages.dev. This
# is mimicking the default Cloudflare Pages preview environment behavior, when
# production and preview deploys are triggered by a git push (as a side note,
# our setup doesn't use this git integration, and rather deploys manually
# through `wrangler`).
#
# Alternatively, you can pass a custom branch name to this script. This is
# useful for maintaining a stable review URL across time for a complex feature
# spread across multiple stacked PRs. For example, if you want to deploy to
# https://donate.owid.pages.dev, while on the `donate-2` branch you can run:
#
# PREVIEW_BRANCH=donate yarn deploy-content-preview
#
# Note that with or without the PREVIEW_BRANCH argument, this script will
# deploy from the staging server matching your current git branch name. For
# example, if you're on the `donate` branch, the script will SSH into the
# `staging-site-donate` staging server and execute the deployment commands from
# there.
#
# # SETUP (only once)
#
# ## RE-BAKING CONTENT WITH THE CORRECT BAKED_BASE_URL
#
# This script doesn't bake content, it only deploys the already baked content
# in ~/live-date/bakedSite/ after performing some post-bake transformation
# steps, and storing the result in ~/dist.
#
# Originally, the content is baked for the staging URL (e.g.
# http://staging-site-donate).
#
# Before running this script, you need to rebake the content using the target
# preview BAKED_BASE_URL (e.g. https://donate.owid.pages.dev) to avoid CORS
# issues. This is done by running the following sequence on the staging server
# (not locally):
# - in ~/owid-grapher/.env, set BAKED_BASE_URL to
# https://[PREVIEW_BRANCH].owid.pages.dev
# - `rm ~/.site-baked` (see ops > templates > lxc-manager > grapher-refresh)
# - locate the latest Buildkite job for your staging environment and click
# "Rebuild". This will run the full site baking sequence with the preview URL
# as BAKED_BASE_URL.
#
# You don't need to continuously rebake the site to test iterative changes to
# functions code in the preview environment. Functions are not baked, they are
# rather merely copied as-is to the `dist` folder to be executed on the server.
# When git pushing functions code, the regular staging job will automatically
# update them on the staging server. You then just need to run `yarn
# deploy-content-preview` to deploy the updated functions code to the preview
# URL.
#
# ## CREATING CLOUDFLARE PAGES API TOKEN (optional)
#
# This script requires a Cloudflare API token to deploy to the preview URL.
#
# In particular, `wrangler` uses CLOUDFLARE_ACCOUNT_ID and
# CLOUDFLARE_API_TOKEN, read from owid-grapher/.env on the staging server.
#
# These environment variables should be set automatically by the staging server
# creation script. If you need to create them manually, follow these steps:
#
# - CLOUDFLARE_ACCOUNT_ID: You can find your account ID at
# https://dash.cloudflare.com/ > Our World in Data. The account ID is in the
# URL, e.g. https://dash.cloudflare.com/1234567890abcdef1234567890abcdef
#
# - CLOUDFLARE_API_TOKEN: You can create your token at
# https://dash.cloudflare.com/profile/api-tokens. Use the "Create Custom Token"
# template, then give it the "Account > Cloudflare Pages > Edit" permission.
# Use a TTL corresponding to the duration of your test (e.g. 2 weeks).
#
# Save both these values in owid-grapher/.env on the staging server (not
# locally).


set -o errexit
set -o pipefail
set -o nounset

GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
HOST="staging-site-$GIT_BRANCH"

as_owid() {
ssh -t -o "LogLevel=QUIET" -o "StrictHostKeyChecking=no" "owid@$HOST" "$@"
}

# Cloudflare Pages settings
PROJECT_NAME=owid

# Set PREVIEW_BRANCH to the current git branch name if not set
PREVIEW_BRANCH=${PREVIEW_BRANCH:-$GIT_BRANCH}

# URL of Cloudflare page
BAKED_BASE_URL="https://$PREVIEW_BRANCH.$PROJECT_NAME.pages.dev"

deploy_site () {
echo "--- Deploy site to $BAKED_BASE_URL from $HOST"

create_dist
deploy_to_cloudflare

echo "--- Site deployed to $BAKED_BASE_URL from $HOST"
}

create_dist() {
echo '--- Creating dist/ folder'
# Define a list of excluded directories for rsync
EXCLUDES=(grapher/data/variables/ .git/ grapher/exports/ exports/ uploads/)

# Build rsync command with excluded directories
RSYNC_COMMAND=("rsync" "-havzq" "--delete")
for EXCLUDE in "${EXCLUDES[@]}"; do
RSYNC_COMMAND+=("--exclude=$EXCLUDE")
done

as_owid "${RSYNC_COMMAND[@]}" live-data/bakedSite/ dist/

# remove .etag of gdoc images from dist
as_owid rm dist/images/published/*.etag

# copy over Pages Functions as-is
as_owid rsync -havzq --delete owid-grapher/functions dist/

# we need node_modules for Pages Functions
as_owid rm -rf dist/node_modules
as_owid cp -rL owid-grapher/node_modules dist/ # the `L` flag copies over symlinked files, too, which we need for @ourworldindata/utils etc.

as_owid cp owid-grapher/_routes.json dist/_routes.json
}

deploy_to_cloudflare() {
echo '--- Deploy to Cloudflare'
# - Using the `--branch [PREVIEW_BRANCH]` argument creates a deploy preview.
# This doesn't affect production.
#
# Some gotchas:
# - trim \r, they remove previous logs in buildkite
# - functions are not deployed if wrangler is not run from the folder
# containing the functions at the root (e.g. `wrangler pages deploy dist`
# fails to deploy functions, silently)
# https://community.cloudflare.com/t/cf-pages-functions-wrangler-dev-405-method-not-allowed-locally/442767/15
# - we cannot run `yarn wrangler` from the dist folder because it doesn't
# contain a package.json
# - we cannot run `./node_modules/.bin/wrangler` as it fails to resolve some
# paths (similar to running `npx wrangler` when wrangler is present
# locally in node_modules after copying it from owid-grapher with `cp -rL`)
as_owid "cd dist && env \$(grep \"^CLOUDFLARE\" ../owid-grapher/.env | xargs -0) ./node_modules/wrangler/bin/wrangler.js pages deploy . --project-name "$PROJECT_NAME" --branch "$PREVIEW_BRANCH" 2>&1 | tr -d '\r'"
}

deploy_site
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"bakeGdocPosts": "node --enable-source-maps ./itsJustJavascript/baker/bakeGdocPosts.js",
"cleanTsc": "rm -rf itsJustJavascript && tsc -b -clean",
"deployWordpress": "./wordpress/scripts/deploy.sh",
"deployContentPreview": "PREVIEW_BRANCH=${PREVIEW_BRANCH:-} ./ops/buildkite/deploy-content-preview",
"fetchServerStatus": "node --enable-source-maps ./itsJustJavascript/baker/liveCommit.js",
"fixLint": "eslint . --fix",
"fixPrettierAll": "yarn prettier --write \"**/*.{tsx,ts,jsx,js,json,md,html,css,scss,yml}\"",
Expand Down

0 comments on commit efe4bd7

Please sign in to comment.