| App | Preview Worker | Production Worker | Domain attach |
|---|---|---|---|
playground |
sketchi-playground-pr-<number> |
sketchi-playground |
playground.sketchi.app |
studio |
sketchi-studio-pr-<number> |
sketchi-studio |
studio.sketchi.app |
web |
sketchi-web-pr-<number> |
sketchi-web |
sketchi.app, www.sketchi.app |
excalidraw |
sketchi-excalidraw-pr-<number> |
sketchi-excalidraw |
excalidraw.sketchi.app |
icons |
sketchi-icons-pr-<number> |
sketchi-icons |
icons.sketchi.app |
flowchart LR
PR["Pull request"] --> Matrix["preview matrix"]
Matrix --> Build["pnpm nx build <app>"]
Build --> Config["generated preview wrangler config"]
Config --> Deploy["wrangler deploy --keep-vars --no-x-provision"]
Deploy --> Comment["sticky PR comment"]
Closed["PR closed"] --> Cleanup["delete preview Worker"]
Pull requests to main deploy matrix apps to PR-specific Cloudflare Workers.
- uses the same pnpm 11.5.0, Node 24,
pnpm install --frozen-lockfilesetup asv2-ci; - builds each Nx app in an isolated matrix job;
- writes a generated
dist/server/wrangler.<app>.preview.jsonwith the preview Worker name and no custom production routes; - runs
wrangler deploy --keep-vars --no-x-provision; - writes or updates one sticky PR comment per app with the preview URL.
For the web preview, the workflow also reads the account workers.dev
subdomain from Cloudflare and injects sibling preview URLs into the generated
Wrangler vars:
SKETCHI_EXCALIDRAW_URLSKETCHI_ICONS_URLSKETCHI_PLAYGROUND_URL
That keeps Web preview navigation inside the same PR's preview Workers instead of sending reviewers to production domains.
CHROMATIC_PROJECT_TOKEN:stagingenvironment secret for Storybook publish and visual checks.GRAPHITE_TOKEN: repository secret for the Graphite CI optimizer jobs inv2-ciand preview deploy workflows.CLOUDFLARE_ACCOUNT_ID:stagingenvironment variable or secret.CLOUDFLARE_API_TOKEN:stagingenvironment secret with Workers edit/deploy access.
| Source | Target | Purpose |
|---|---|---|
Infisical sketchi /github staging |
GitHub staging environment |
CI, Graphite optimizer, and PR preview deploys |
Infisical sketchi /github prod |
GitHub production environment |
production deploys |
The canonical source for those GitHub Actions values is the Infisical sketchi
project under /github, synced to GitHub environment secrets:
staging: GitHubstagingenvironment for CI and PR preview deploys.prod: GitHubproductionenvironment for production deploys.
Do not sync both Infisical environments into the same repository-secret namespace; environment-scoped GitHub secrets keep preview and production values from overwriting each other when the values eventually diverge.
Cloudflare documents that non-interactive CI deploys require an API token and
account ID. The token should stay in GitHub Secrets, not in source control.
Preview deploys pass --no-x-provision so CI only uploads explicit Worker
configuration. Preview deploys must not create, discover, or mutate KV, D1, or
R2 resources.
Cleanup runs automatically when a PR closes and deletes the PR-specific Worker. Manual cleanup is also available:
CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_API_TOKEN=... \
node scripts/04-delete-preview-worker.mjs --pr-number 123The deploy command scripts are numbered because they are operational steps:
scripts/01-prepare-preview-deploy.mjsscripts/02-extract-preview-url.mjsscripts/03-upsert-preview-comment.mjsscripts/04-delete-preview-worker.mjsscripts/05-prepare-production-domain-deploy.mjs
Pass --app playground, --app studio, --app web, --app excalidraw, or
--app icons to the prepare and cleanup scripts when running them manually.
The app-production-deploy workflow runs on pushes to main and deploys the
five wired production Workers without assigning final custom domains. Production
deploys also pass --no-x-provision; Workers may bind existing resources, but
CI deploys do not create or discover storage.
Those deploys keep workers_dev enabled so the app can be verified from
Cloudflare-owned workers.dev URLs before any DNS or registrar cutover.
The Studio Worker binds Code Mode artifacts to R2 and Code Mode usage analytics to Cloudflare Pipelines:
| Surface | Binding | Remote target |
|---|---|---|
| Studio preview Workers | SKETCHI_ARTIFACTS |
sketchi-studio-codemode-artifacts-preview |
| Studio production Worker | SKETCHI_ARTIFACTS |
sketchi-studio-codemode-artifacts-production |
| Studio preview Workers | CODEMODE_USAGE_EVENTS |
e9fc3bcd35314fa39fc6a89018207acc |
| Studio preview Workers | CODEMODE_USAGE_ISSUES |
d95a1767edf246af8c637c5b9bf5a5c5 |
| Studio production Worker | CODEMODE_USAGE_EVENTS |
d9044253316f4273a60298098f444a62 |
| Studio production Worker | CODEMODE_USAGE_ISSUES |
f687dab6e7d742c1a76834089e709462 |
Preview Wrangler configs rewrite the Studio artifact binding to the preview
bucket and the Studio Pipeline bindings to preview streams. Production deploys
keep production buckets and production streams. All Worker-bound buckets and
streams must exist before their Workers deploy. The downstream R2 Data Catalog
sinks are long-lived Cloudflare Pipeline resources, not Worker bindings. They
write into sketchi-codemode-usage-analytics-production-v4 and
sketchi-codemode-usage-analytics-preview-v4; run
pnpm r2sql:codemode:resources to print the sink, pipeline, bucket, and table
map. Preview deploys disable Wrangler resource provisioning, so the CI token
does not need R2 object read access just to deploy the Worker.
R2 Data Catalog verification must prove an aggregate R2 SQL data scan through
the direct R2 SQL API, not just SHOW TABLES or DESCRIBE, because
metadata-only catalog calls can succeed before rows are queryable. The
2026-06-29 real end-to-end check called production and preview Studio APIs with
unique x-sketchi-run-id headers, then verified both usage_events and
usage_issues rows in the v4 catalog buckets with
pnpm r2sql:codemode:verify-run -- --require-issues. The verifier polls R2
SQL by default. For normal successful API runs, issue rows may be absent; the
command only requires issue rows when --require-issues or explicit
issue-run-id options are supplied.
Wrangler accepts Pipeline stream names in local dry-runs, but the deploy API
requires stream IDs for Worker bindings. Keep apps/studio/wrangler.jsonc on
the production stream IDs and keep the preview deploy helper's ID rewrite table
in sync with Cloudflare Pipeline stream creation.
Assigning sketchi.app, www.sketchi.app, playground.sketchi.app,
studio.sketchi.app, excalidraw.sketchi.app, and icons.sketchi.app is
intentionally manual. Run the app-production-deploy workflow with
attach_domains enabled only when the new site is ready to own those hostnames.
The manual step writes a generated domain Wrangler config from
scripts/05-prepare-production-domain-deploy.mjs and deploys that route-bearing
config.