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
21 changes: 11 additions & 10 deletions INFRASTRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This repo is the **umbrella** of OWID's technical documentation. Its content is small (a landing page plus links into the subprojects). What makes it special is that it owns the custom domain and acts as a **router** in front of subproject Pages projects, so one URL transparently serves docs from multiple GitHub repos.

```
docs-cf.owid.io (later: docs.owid.io)
docs.owid.io (staging alias: docs-cf.owid.io)
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
Expand Down Expand Up @@ -34,7 +34,7 @@ This repo is the **umbrella** of OWID's technical documentation. Its content is

Each subproject repo owns:
- a `.github/workflows/deploy-docs-cf.yml` that builds the docs and uploads to its Pages project via `wrangler pages deploy`
- a sed-override of `site_url` in CI so the canonical/sitemap URLs reflect the production path on `docs-cf.owid.io/projects/<short>/`
- a sed-override of `site_url` in CI so the canonical/sitemap URLs reflect the production path on `docs.owid.io/projects/<short>/`

Per-PR previews still work on each project's own `*.pages.dev` URL β€” the umbrella router is only invoked for traffic to the custom domain.

Expand All @@ -44,9 +44,10 @@ Lives at repo root, copied into `site/` by the deploy workflow so CF Pages picks

Responsibilities, in order:

1. **Legacy URL rewrite** β€” strip the `/en/latest` segment baked into old ReadTheDocs links and 301 to the canonical path.
2. **Subproject routing** β€” match a `SUBPROJECTS` prefix, `fetch()` the same path on the target Pages project, and stream the response back.
3. **Fallback** β€” `env.ASSETS.fetch(request)` serves this repo's own static assets (umbrella landing page, CSS, etc.).
1. **COVID docs redirect** β€” `/projects/covid/*` 301s to the legacy COVID docs at their native ReadTheDocs URL (`owidcovid-19-data.readthedocs.io`); they were never migrated to CF Pages. Runs before the `/en/latest` rewrite because RtD needs the version segment intact.
2. **Legacy URL rewrite** β€” strip the `/en/latest` segment baked into old ReadTheDocs links and 301 to the canonical path.
3. **Subproject routing** β€” match a `SUBPROJECTS` prefix, `fetch()` the same path on the target Pages project, and stream the response back.
4. **Fallback** β€” `env.ASSETS.fetch(request)` serves this repo's own static assets (umbrella landing page, CSS, etc.).

To add a new subproject, append one line to the `SUBPROJECTS` map.

Expand All @@ -61,7 +62,7 @@ Concrete example: a hypothetical `owid/foo-docs` repo serving its docs at `docs.
```

2. **Add a workflow** in `owid/foo` mirroring [`.github/workflows/deploy-docs-cf.yml`](./.github/workflows/deploy-docs-cf.yml) from this repo or from `owid/etl`. Two things to adjust:
- sed-replace `site_url` to `https://docs-cf.owid.io/projects/foo/` (later `docs.owid.io/projects/foo/`).
- sed-replace `site_url` to `https://docs.owid.io/projects/foo/`.
- Stage the build into `staging/projects/foo/` before `wrangler pages deploy`.

3. **Add repo secrets** in `owid/foo`: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID` (same values used by the other repos).
Expand All @@ -88,7 +89,7 @@ CF dashboard β†’ **owid.io zone** β†’ Caching β†’ Configuration β†’ Purge Cache

### Swapping the custom domain

Currently `docs-cf.owid.io` is bound to the `owid-docs` Pages project. To re-attach to another project (e.g., during emergency rollback), detach from the source first, wait ~30 s, then attach to the target.
`docs.owid.io` (plus the staging alias `docs-cf.owid.io`) is bound to the `owid-docs` Pages project. To re-attach to another project (e.g., during emergency rollback), detach from the source first, wait ~30 s, then attach to the target.

### Production branch

Expand All @@ -103,7 +104,7 @@ Each subproject repo + this one needs:

One token + account ID pair works for all repos.

## Related deployments (unchanged today)
## Related deployments (legacy)

- **ReadTheDocs** β€” still serves `https://docs.owid.io/` and the subprojects. Builds untouched. Will be decommissioned once the CF mirror has been validated and `docs.owid.io` DNS is repointed.
- **GitHub Pages** β€” `.github/workflows/docs.yml` in this repo still publishes a copy. No active consumer; can be removed after the cut-over.
- **ReadTheDocs** β€” no longer serves `https://docs.owid.io/` (DNS was repointed to CF Pages). The RtD projects (`owid-docs`, `owid-etl`, `owid-grapher-py`) can be archived once the cut-over has been stable for a while. The **COVID docs** stay on RtD indefinitely at `https://owidcovid-19-data.readthedocs.io/` β€” `_worker.js` redirects `/projects/covid/*` there. The `docs.owid.io` custom domain must stay removed from the RtD `owid-docs` project, otherwise RtD redirects the COVID docs' native URL back to `docs.owid.io` and the redirect loops.
- **GitHub Pages** β€” `.github/workflows/docs.yml` in this repo still publishes a copy. No active consumer; can be removed now that the cut-over is done.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

Umbrella site for OWID's technical documentation.

Production URL during the parallel-hosting phase: **https://docs-cf.owid.io/**.
The current production URL on ReadTheDocs is unchanged: **https://docs.owid.io/**.
Production URL: **https://docs.owid.io/** (served from Cloudflare Pages).
`docs-cf.owid.io` remains attached as a staging alias from the parallel-hosting phase.

## What's in this repo

Expand All @@ -13,8 +13,8 @@ The current production URL on ReadTheDocs is unchanged: **https://docs.owid.io/*
| `zensical.toml` | Site config (Zensical / Material) |
| `_worker.js` | Cloudflare Pages worker β€” proxies subproject paths to other Pages projects (`/projects/etl/*`, `/projects/owid-grapher-py/*`, …) |
| `.github/workflows/deploy-docs-cf.yml` | Builds + deploys this site to Cloudflare Pages on every push to `main` |
| `.github/workflows/docs.yml` | Legacy GitHub Pages deploy (will be removed after the RtD cut-over) |
| `.readthedocs.yml` | RtD build config (unchanged during the transition) |
| `.github/workflows/docs.yml` | Legacy GitHub Pages deploy (to be removed now that the RtD cut-over is done) |
| `.readthedocs.yml` | Legacy RtD build config (to be removed once the cut-over has been stable for a while) |
| `INFRASTRUCTURE.md` | How the CF deployment fits together + how to add a new subproject |

## Local development
Expand Down
21 changes: 19 additions & 2 deletions _worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// When present at the deployment root, this file intercepts ALL requests
// for this Pages project. We use it as a thin router so that one custom
// domain (docs-cf.owid.io and later docs.owid.io) can transparently serve
// docs from multiple source repos:
// domain (docs.owid.io) can transparently serve docs from multiple
// source repos:
//
// /projects/etl/* β†’ proxied to https://owid-etl-docs.pages.dev/projects/etl/*
// everything else β†’ served from this project's own static assets
Expand All @@ -17,6 +17,15 @@ const SUBPROJECTS = {
"/projects/owid-grapher-py/": "https://owid-grapher-py-docs.pages.dev",
};

// The legacy COVID docs were never migrated to CF Pages β€” they stay on
// ReadTheDocs at their native URL. This must be checked BEFORE the
// /en/latest rewrite below: RtD needs the version segment intact.
// NOTE: requires docs.owid.io to be removed as custom domain from the
// RtD project at cut-over, otherwise RtD bounces the native URL back
// here and we loop.
const COVID_PREFIX = "/projects/covid";
const COVID_ORIGIN = "https://owidcovid-19-data.readthedocs.io";

// Legacy ReadTheDocs URLs include an /en/latest segment (e.g.
// /projects/etl/en/latest/, /en/latest/). 301 to the canonical form so
// existing inbound links from blog posts / Slack / bookmarks keep working.
Expand All @@ -26,6 +35,14 @@ export default {
async fetch(request, env) {
const url = new URL(request.url);

if (
url.pathname === COVID_PREFIX ||
url.pathname.startsWith(`${COVID_PREFIX}/`)
) {
const rest = url.pathname.slice(COVID_PREFIX.length);
return Response.redirect(`${COVID_ORIGIN}${rest}${url.search}`, 301);
}

if (RTD_LEGACY.test(url.pathname)) {
url.pathname = url.pathname.replace(RTD_LEGACY, "$1") || "/";
return Response.redirect(url.toString(), 301);
Expand Down
Loading