Deploy Environments (Dev / Production)
Neemias uses two deployment environments controlled by the DEPLOY_ENV build-time variable.
Git hooks (Husky)
O projeto usa Husky para hooks versionados (instalado automaticamente via pnpm install):
| Hook | Arquivo | O que faz |
|---|---|---|
pre-commit | .husky/pre-commit | 6 gates: lint-staged, as any regression, Biome check, typecheck, doc lint, unit tests, E2E smoke |
pre-push | .husky/pre-push | Doc drift detection + pre-deploy check |
Se qualquer gate falhar, o commit ou push e bloqueado com a mensagem do erro.
Environment matrix
| Environment | DEPLOY_ENV | Seed runs? | URL | Backend API |
|---|---|---|---|---|
| Dev (local) | dev | ✅ Full seed | localhost:5173 | (none — offline-only) |
| Production | production | ❌ Skipped | app.neemias.app | api.neemias.app |
Important:
app.neemias.appandapi.neemias.appare production. They must haveAUTH_MODE=jwt,ENVIRONMENT=production, andDEPLOY_ENV=productionconfigured in Cloudflare Dashboard. See production-checklist.md.
Deploy script
scripts/deploy.sh automates the build:
./scripts/deploy.sh # production (default: NODE_ENV=production)The script executes:
pnpm -r test— all monorepo testsNODE_ENV=production DEPLOY_ENV=production pnpm build:app— production build with hash-based CSPnpx wrangler deploy— Worker deploynpx wrangler pages deploy app/dist --project-name neemias— frontend deploy
How it works
Frontend (app/vite.config.ts)
define: {
__DEPLOY_ENV__: JSON.stringify(process.env.DEPLOY_ENV ?? "dev"),
}When DEPLOY_ENV is not set (local dev, or Pages secret not configured), the default is "dev" — the seed runs normally.
Seed guard (app/src/db/seed.ts)
const deployEnv = typeof __DEPLOY_ENV__ !== "undefined" ? __DEPLOY_ENV__ : "dev";
if (deployEnv === "production") {
return; // no demo data in production
}Backend seed (workers/src/routes/seed.ts)
The worker seed (POST /api/v1/_seed) is blocked when ENVIRONMENT=production is set in the Worker environment variables.
Configuring Cloudflare
Production
Set these in Cloudflare Dashboard:
| Service | Variable | Value | Type |
|---|---|---|---|
Worker (api.neemias.app) | AUTH_MODE | jwt | Plain text |
Worker (api.neemias.app) | AUTH_JWT_SECRET | openssl rand -hex 32 | Secret |
Worker (api.neemias.app) | ENVIRONMENT | production | Plain text |
Pages (app.neemias.app) | DEPLOY_ENV | production | Plain text |
Data flow per environment
┌──────────┐ ┌─────────────────────┐ ┌──────────────────────┐
│ Dev │────▶│ Frontend SQLite WASM│◀────│ Worker D1 (seed) │
│ (local) │ │ demo data │ │ demo data │
└──────────┘ └─────────────────────┘ └──────────────────────┘
┌──────────┐ ┌─────────────────────┐ ┌──────────────────────┐
│ Prod │────▶│ Frontend SQLite WASM│◀────│ Worker D1 (vazio) │
│(no seed) │ │ Só dados reais │ │ Só dados reais │
└──────────┘ └─────────────────────┘ └──────────────────────┘In dev (local), both the frontend and worker can be seeded with demo data. In production, neither seed runs. All data comes from real usage.
Pre-deploy checklist
Before any production deployment, run:
bash scripts/pre-deploy-check.shThis validates:
AUTH_MODEis notdevapp/public/_headersexists and uses hash-based CSP (no nonces)scripts/validate-csp.shpasses (all required origins in connect-src)workers/wrangler.tomlexistsmigrations/directory existsAUTH_JWT_SECRETis set (warning if missing)
See also: