ThinkEx deploys from Cloudflare Workers Builds. GitHub Actions only validates pull requests. Deploy, migration, and secret-management commands are operational actions and should not be run casually from a local checkout.
Branches and Environments
The CI workflow runs on pull requests only. It installs with Vite+ and runs check, test, and a staging-shaped build:
ciBuildStaging compiles with CLOUDFLARE_ENV=staging so PR CI exercises real Wrangler env flattening. PostHog build secrets are optional for that path (analytics stays off when unset); production deploy still requires the full production PostHog set, including the feedback survey id. Local vp run verify stays on the generic ciBuild (build:app) so contributors without Infisical are unaffected. When changes go through a pull request, the validate check must be green before merge.
The separate React Doctor workflow runs on pull requests and pushes to main. It scans changed React files on pull requests, posts inline diagnostics, and records the React Doctor score without folding those comments into the main Vite+ verification job.
Deploy
Two Workers Builds configurations watch this repository, one per branch. Each runs a build command, then a deploy command that applies Postgres migrations before shipping the Worker:
CLOUDFLARE_ENV is set per configuration in the Cloudflare dashboard, which is why deploy:worker takes no --env flag. Running it from a local checkout without that variable targets the thinkex-dev Worker, not production.
Build variables and secrets (Cloudflare API token, PostHog production set) live in the Workers Builds settings. Each deployment configuration also needs the environment’s direct PlanetScale administrative connection string as the build-time DATABASE_URL; it is used only by Drizzle Kit and is not a Worker runtime secret. The Worker itself connects with the environment’s cache-disabled HYPERDRIVE binding and a restricted runtime database role.
Container Images
Production and staging pin container images to immutable registry.cloudflare.com digests (wrangler.jsonc), so ordinary deploys never need Docker and Workers Builds can run without a Docker daemon. The top-level config still points at the Dockerfiles for wrangler dev.
The Dockerfiles change rarely, so there is no workflow for this. When one does change, rebuild and repin by hand from a machine with Docker:
Copy the digest from the second command into the containers blocks for both staging and production in wrangler.jsonc, then push. Both environments intentionally share one image per container.
Remote migrations and deploy commands affect shared environments. Run them only when you are intentionally doing release work.
Secrets
Runtime and deploy secrets originate in Infisical. Build-time secrets (Cloudflare credentials, PostHog variables) are set in each Workers Builds configuration; runtime secrets declared by the Worker configuration are set on the Worker itself. GitHub only holds what PR CI needs.
Direct workspace uploads additionally require bucket-scoped R2 Object Read & Write credentials in R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY, R2_ACCOUNT_ID, and an independent random WORKSPACE_UPLOAD_TOKEN_SECRET for completion claims. Configure browser PUT access from the checked-in policies:
After inspecting existing lifecycle rules, add a one-day expiry for abandoned direct-upload objects in each bucket:
CORS set replaces the bucket’s current CORS policy. Reconcile the checked-in policy with any existing rules before applying it.