Deploy docs through Cloudflare Pages#1342
Conversation
📝 WalkthroughWalkthroughCloudflare Pages deployment documentation is updated, a routing Worker and Wrangler configuration are added for documentation paths, and the previous GitHub Actions deployment workflow is removed. ChangesDocs hosting and routing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant RouterWorker
participant DocsPages
Browser->>RouterWorker: Request documentation path
RouterWorker->>DocsPages: Proxy eligible GET or HEAD request
DocsPages-->>RouterWorker: Return response or redirect
RouterWorker-->>Browser: Return response with rewritten location
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying openspec-docs with
|
| Latest commit: |
461953e
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://494f3a89.openspec-docs.pages.dev |
| Branch Preview URL: | https://agent-deploy-docs-cloudflare.openspec-docs.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/cloudflare/router/worker.js`:
- Around line 12-23: Update the proxy request construction in the worker to
allow only an explicit safe-header allowlist, excluding sensitive headers such
as Cookie and Authorization, rather than copying all request headers. Reject
non-GET/HEAD requests before creating the proxy request, and remove the
init.body assignment and write-path handling.
In `@website/cloudflare/router/wrangler.jsonc`:
- Around line 7-14: Update the route handling in worker.js to gate proxying by
exact pathname allowlists for the docs, api/search, and llms route groups.
Preserve query-string support while rejecting unexpected path prefixes that
match the broad Wrangler patterns, and apply the same validation consistently
before forwarding requests to Pages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: aa54cabc-98d5-4fd9-9ca5-fd055e0d8cca
📒 Files selected for processing (4)
.github/workflows/deploy-docs.ymlwebsite/README.mdwebsite/cloudflare/router/worker.jswebsite/cloudflare/router/wrangler.jsonc
💤 Files with no reviewable changes (1)
- .github/workflows/deploy-docs.yml
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
website/cloudflare/router/worker.js (1)
52-69: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winWrap the upstream fetch in error handling.
If
openspec-docs.pages.devis unreachable or returns a network error, theawait fetchon line 52 will throw and Cloudflare will return an opaque 1101/500 to the client. Atry/catchreturning a 502 with a short message would give users a clearer signal and prevent confusing error pages.🛡️ Suggested error handling
- const response = await fetch(upstream.toString(), init); - const responseHeaders = new Headers(response.headers); - const location = responseHeaders.get('location'); - - if (location) { - const redirected = new URL(location, upstream); - if (redirected.hostname === 'openspec-docs.pages.dev') { - redirected.protocol = incoming.protocol; - redirected.host = incoming.host; - responseHeaders.set('location', redirected.toString()); - } - } - - return new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: responseHeaders, - }); + let response; + try { + response = await fetch(upstream.toString(), init); + } catch { + return new Response('Documentation is temporarily unavailable.', { + status: 502, + headers: { 'content-type': 'text/plain' }, + }); + } + const responseHeaders = new Headers(response.headers); + const location = responseHeaders.get('location'); + + if (location) { + const redirected = new URL(location, upstream); + if (redirected.hostname === 'openspec-docs.pages.dev') { + redirected.protocol = incoming.protocol; + redirected.host = incoming.host; + responseHeaders.set('location', redirected.toString()); + } + } + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: responseHeaders, + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/cloudflare/router/worker.js` around lines 52 - 69, Wrap the upstream request and response-processing logic in the worker’s fetch handler with try/catch, including the existing fetch call and redirect rewriting. On fetch failure, return a 502 Response with a concise plain-text error message instead of allowing the exception to propagate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/cloudflare/router/worker.js`:
- Around line 79-80: The router condition for the exact “/og/docs” path requires
a matching exact deployment route. Update
website/cloudflare/router/wrangler.jsonc to add “openspec.dev/og/docs” alongside
the existing wildcard route, preserving the “/og/docs/*” coverage.
---
Nitpick comments:
In `@website/cloudflare/router/worker.js`:
- Around line 52-69: Wrap the upstream request and response-processing logic in
the worker’s fetch handler with try/catch, including the existing fetch call and
redirect rewriting. On fetch failure, return a 502 Response with a concise
plain-text error message instead of allowing the exception to propagate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b6f3334d-82f6-4ed1-acf6-8a668429bed6
📒 Files selected for processing (1)
website/cloudflare/router/worker.js
| pathname === '/og/docs' || | ||
| pathname.startsWith('/og/docs/') || |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether /og/docs (without trailing slash) is expected to be served by the Worker
rg -n 'og/docs' website/cloudflare/router/Repository: Fission-AI/OpenSpec
Length of output: 424
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== wrangler.jsonc =="
cat -n website/cloudflare/router/wrangler.jsonc
echo
echo "== worker.js around isDocsRoute =="
sed -n '60,100p' website/cloudflare/router/worker.js
echo
echo "== any other og/docs references =="
rg -n --context 2 'og/docs' website/cloudflare/router/Repository: Fission-AI/OpenSpec
Length of output: 2890
🌐 Web query:
Cloudflare Workers route pattern "/path/*" match exact "/path" without trailing slash
💡 Result:
In Cloudflare Workers, a route pattern with a trailing asterisk, such as /path/*, does not match an exact path without a trailing slash (e.g., /path) [1]. The wildcard operator (*) in Cloudflare route patterns matches zero or more characters [1]. For the pattern /path/* to trigger, there must be content after the /path/ prefix. Consequently: - /path/* matches /path/ and any path starting with /path/ (e.g., /path/something). - /path/* does not match the exact path /path because the wildcard expects a subsequent character (or a trailing slash) to satisfy the pattern [1]. To match both the exact path /path and any sub-paths under it, you must define two separate route patterns: 1. /path 2. /path/* Cloudflare routing is deterministic: if multiple patterns match, the system selects the most specific one [1][2]. By explicitly defining both, you ensure all variations of that path are covered [1].
Citations:
- 1: https://developers.cloudflare.com/workers/configuration/routing/routes/index.md
- 2: https://community.cloudflare.com/t/cloudflare-worker-route-priority-order/158524
Add openspec.dev/og/docs for the bare path. openspec.dev/og/docs/* doesn’t cover /og/docs, so pathname === '/og/docs' in website/cloudflare/router/worker.js is unreachable unless website/cloudflare/router/wrangler.jsonc adds the exact route.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/cloudflare/router/worker.js` around lines 79 - 80, The router
condition for the exact “/og/docs” path requires a matching exact deployment
route. Update website/cloudflare/router/wrangler.jsonc to add
“openspec.dev/og/docs” alongside the existing wildcard route, preserving the
“/og/docs/*” coverage.
alfred-openspec
left a comment
There was a problem hiding this comment.
Reviewed the Cloudflare Pages deployment and routing changes. The route scoping, request-header allowlist, method restrictions, redirect handling, and deployment documentation look good, and all checks are passing.
Summary
openspec.dev/docsand proxies its supporting asset/search/OG/LLM routesopenspec-docsPages build and deployment setupWhy
The Fumadocs site is a static export and now deploys directly through Cloudflare Pages' Git integration. The existing landing page remains in its separate Astro project, so a small Worker provides path-based routing on the shared
openspec.devhostname without requiring Enterprise-only Origin Rule host/DNS overrides.Deployment behavior
mainrebuild production only whendocs/**orwebsite/**changesValidation
NEXT_PUBLIC_SITE_URL=https://openspec.dev pnpm run build(87 static routes)node --check cloudflare/router/worker.jswrangler deploy --dry-run --config cloudflare/router/wrangler.jsonc/,/docs,/docs/core-concepts,/api/search, and/llms.txtSummary by CodeRabbit
New Features
GET/HEADfor routed documentation paths.Documentation
NEXT_PUBLIC_SITE_URLexample.Chores