diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index b1e25766c..5ce166216 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -15,9 +15,9 @@ on: - main workflow_dispatch: -# Pages publication is confined to `deploy-docs`; the build job only reads the -# repository. Keeping `pages`/`id-token` off the workflow level means the -# notebook execution step never runs with a deployment-capable token. +# Publication is confined to `deploy-docs`; the build job only reads the +# repository. Keeping the Cloudflare token out of the build job means the +# notebook execution step never runs with a deployment-capable credential. permissions: {} concurrency: @@ -114,10 +114,13 @@ jobs: fi continue-on-error: true - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + - name: Upload the docs build + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: + name: docs-html path: docs/_build/html + if-no-files-found: error + retention-days: 7 deploy-docs: needs: build-docs @@ -129,15 +132,31 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 permissions: - pages: write # deploy to GitHub Pages - id-token: write # OIDC verification of the deployment + contents: read # checkout, for docs/wrangler.jsonc concurrency: - group: pages + group: cloudflare-docs cancel-in-progress: false - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} steps: - - name: Deploy documentation - id: deployment - uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 + - name: Check out the code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Download the docs build + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: docs-html + path: docs/_build/html + + - name: Set up Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + + # Cloudflare serves the docs as the static assets of the `gpjax-docs` + # Worker (docs/wrangler.jsonc). The token lives only in this job. + - name: Deploy to Cloudflare + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} + run: npx --yes wrangler@4.145.0 deploy --config docs/wrangler.jsonc diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 9f0176801..000000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -gpjax.quantclimate.com diff --git a/docs/_redirects b/docs/_redirects new file mode 100644 index 000000000..df8e0e455 --- /dev/null +++ b/docs/_redirects @@ -0,0 +1,5 @@ +# Cloudflare static-asset rules, copied to the site root by html_extra_path. +# html_handling is "none" in docs/wrangler.jsonc, so a directory URL only +# serves its index.html through these rules. +/ /index.html 200 +/*/ /:splat/index.html 200 diff --git a/docs/conf.py b/docs/conf.py index f55f64232..cdf6d3bc9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -303,7 +303,7 @@ html_theme = "shibuya" html_title = "GPJax" # Sitemap, canonical link, og:url and the absolute og:image URL all derive from -# this, so it must match the domain GitHub Pages is actually serving (docs/CNAME). +# this, so it must match the custom domain in docs/wrangler.jsonc. html_baseurl = "https://gpjax.quantclimate.com/" sitemap_url_scheme = "{link}" html_static_path = ["stylesheets"] @@ -311,9 +311,10 @@ # Folds the Migrations sidebar group after shibuya's own sidebar script has run. # See the file header for why this cannot be expressed in theme options. html_favicon = "static/favicon.ico" -# GPJax serves the docs from a custom domain, so GitHub Pages needs a CNAME file -# at the site root. impulso has no counterpart: it deploys to a github.io path. -html_extra_path = ["CNAME"] +# Cloudflare serves the docs with html_handling "none" (docs/wrangler.jsonc), so +# a directory URL, such as / or a sphinx-reredirects stub, only serves its +# index.html through the rules in _redirects. +html_extra_path = ["_redirects"] html_theme_options = { # `accent_color` only accepts a radix ramp *name*. shibuya writes the value # verbatim into `` and its stylesheet carries diff --git a/docs/wrangler.jsonc b/docs/wrangler.jsonc new file mode 100644 index 000000000..4cab9ce0c --- /dev/null +++ b/docs/wrangler.jsonc @@ -0,0 +1,18 @@ +// Cloudflare Workers configuration for the docs: the Sphinx HTML build is +// served as static assets, with no Worker code. The `deploy-docs` job in +// .github/workflows/build_docs.yml deploys it on every push to main. +{ + "name": "gpjax-docs", + "compatibility_date": "2026-09-30", + // Serve the docs only on the custom domain, not on workers.dev. + "workers_dev": false, + "preview_urls": false, + // Cloudflare creates the DNS record and certificate for this hostname. + "routes": [{ "pattern": "gpjax.quantclimate.com", "custom_domain": true }], + "assets": { + "directory": "./_build/html", + // Serve every file at exactly the URL Sphinx gives it (/page.html stays + // /page.html). docs/_redirects maps / and /dir/ to their index.html. + "html_handling": "none" + } +} diff --git a/legacy-docs-redirect/README.md b/legacy-docs-redirect/README.md index 43312130b..67870cc3a 100644 --- a/legacy-docs-redirect/README.md +++ b/legacy-docs-redirect/README.md @@ -1,6 +1,6 @@ # Legacy docs domain redirect -The documentation moved to `gpjax.quantclimate.com` (GitHub Pages). This +The documentation moved to `gpjax.quantclimate.com` (Cloudflare). This directory is the entire contents of the Netlify site that keeps the **old** host, `docs.jaxgaussianprocesses.com`, alive as a permanent redirect. @@ -9,8 +9,8 @@ hand, once, and then only again if the redirect target changes. ## Why this exists -GitHub Pages serves exactly one custom domain per site, so it cannot answer for -both hosts. Without this, every published link to the old domain 404s — the JOSS +The docs Worker can only serve hostnames in the `quantclimate.com` Cloudflare +zone, so it cannot answer for the old host. Without this, every published link to the old domain 404s — the JOSS paper, the PyPI project page, search results, and any third-party citation. ## Deploying it