Skip to content

Commit c96056c

Browse files
authored
JANDES-80: Environment setup- local/staging/prod config pattern
1 parent f5c9bff commit c96056c

File tree

7 files changed

+115
-55
lines changed

7 files changed

+115
-55
lines changed

.changeset/gentle-worms-fry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"happy-harmony": minor
3+
---
4+
5+
JANDES-80: Set up env strategy

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
CI=
2+
VITE_SENTRY_DSN=
23
VITE_SENTRY_ORG=
34
VITE_SENTRY_PROJECT=
45
SENTRY_AUTH_TOKEN=

.wrangler/deploy/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"configPath":"../../dist/server/wrangler.json","auxiliaryWorkers":[]}

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Guidance for AI agents (Cursor/Claude) working in this repo.
5858

5959
## Git & PR workflow
6060

61-
- Branch per PR; target `main`.
61+
- Branch per PR; target `mainline`.
6262
- Keep PRs narrowly scoped for easy review.
6363
- Commit messages: format as `<TICKET-123>: <summary>`; include what changed.
6464
- Changesets are required when packages change; include a brief human-readable summary and bump patch for fixes/non-breaking tweaks, minor for new features. Pipeline will fail if missing.

docs/env.md

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,112 @@
11
# Environment configuration
22

3-
This project separates build-time variables (used by Vite and CI) from runtime variables (provided by Cloudflare Workers).
3+
This project separates **build-time** variables (used by Vite/CI tooling) from **runtime** variables (provided to Cloudflare Workers at runtime).
4+
5+
## Build-time env (Vite/CI only)
6+
7+
- **Purpose:** tooling only (e.g., Sentry sourcemap upload during CI builds).
8+
- **Source:** CI environment variables; optionally `.env.local` for local experimentation.
9+
- **Keys:** `CI`, `VITE_SENTRY_ORG`, `VITE_SENTRY_PROJECT`, `SENTRY_AUTH_TOKEN`, `VITE_SENTRY_DSN`.
10+
- **Validation:** `parseBuildEnv` in `src/config/validation.ts` (used by `vite.config.ts`).
11+
- **Notes:**
12+
- Build-time env should **not** contain runtime secrets like AWS keys or auth secrets.
13+
- Sourcemap upload should typically run **only in CI**.
14+
- `SKIP_ENV_LOAD=true` skips loading `.env*` files in Vite; use sparingly.
15+
16+
## Runtime env (Cloudflare Workers)
17+
18+
- **Purpose:** secrets and bindings used by server/runtime code (auth, SES, etc.).
19+
- **Source (staging/prod):** Cloudflare Workers environment variables/secrets.
20+
- **Source (local):** `.dev.vars` when running `wrangler dev`.
21+
- **Keys:**
22+
- Required: `AUTH_SECRET`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `SES_FROM_EMAIL`
23+
- Optional: `SENTRY_DSN`
24+
- **Access:** `getRuntimeEnv()` from `src/config/runtimeEnv.ts` (**Workers runtime only; do not import in client code**). Non-secret runtime metadata (like environment name) may be read from `process.env` when needed.
25+
- **Validation:** Zod schema in `runtimeEnv.ts`; errors list missing keys but never values.
426

5-
## Build-time env
27+
## Local setup
628

7-
- Purpose: tooling only (e.g., Sentry sourcemap upload in CI).
8-
- Source: `.env.local` (optional) or CI env.
9-
- Keys: `CI`, `VITE_SENTRY_ORG`, `VITE_SENTRY_PROJECT`, `SENTRY_AUTH_TOKEN`.
10-
- Validation: `parseBuildEnv` in `src/config/validation.ts` (used by `vite.config.ts`).
29+
- **Runtime (required for server features):**
30+
1. Copy `.dev.vars.example``.dev.vars`
31+
2. Fill in values (AUTH_SECRET, AWS keys, SES_FROM_EMAIL, etc.)
32+
3. Run `pnpm dev` (uses `wrangler dev` on port 3000)
1133

12-
## Runtime env (Workers)
34+
- **Build-time (optional):**
35+
- Copy `.env.example``.env.local` only if you want to experiment with CI-like Sentry sourcemap uploads locally.
36+
- In normal development, you usually don’t need build-time Sentry upload.
1337

14-
- Purpose: secrets and bindings used by server code at runtime.
15-
- Source: Cloudflare bindings; local via `.dev.vars` when running `wrangler dev`.
16-
- Keys: `AUTH_SECRET`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `SES_FROM_EMAIL`, optional `SENTRY_DSN`.
17-
- Access: `getRuntimeEnv()` from `src/config/runtimeEnv.ts` (Workers runtime only; not for client imports).
18-
- Validation: Zod schema in `runtimeEnv.ts`; errors list missing keys but never values.
38+
- **Commands:**
39+
- `pnpm dev` → Workers-parity local dev via Wrangler
40+
- `pnpm dev:vite` → Vite-only fallback (not Workers-parity; avoid for runtime-dependent work)
1941

20-
## Local setup
42+
## Staging / production
43+
44+
- **Secrets** (e.g., `AUTH_SECRET`, AWS keys) should be set with Wrangler:
45+
- `wrangler secret put AUTH_SECRET`
46+
- `wrangler secret put AWS_ACCESS_KEY_ID`
47+
- `wrangler secret put AWS_SECRET_ACCESS_KEY`
2148

22-
- Build-time: optionally copy `.env.example` to `.env.local` and fill Sentry fields if you need CI-style sourcemap uploads locally.
23-
- Runtime: copy `.dev.vars.example` to `.dev.vars` and fill values; `wrangler dev` will load them.
24-
- Run: `pnpm dev` (uses `wrangler dev` on port 3000). `pnpm dev:vite` is available as a Vite-only fallback.
49+
- **Non-secrets** (e.g., `AWS_REGION`, `SES_FROM_EMAIL`, optional `SENTRY_DSN`) can be set as vars:
50+
- via `wrangler.jsonc` `vars` (preferred when values are non-sensitive), or
51+
- via Cloudflare dashboard for the Worker environment
2552

26-
## Staging/production
53+
- **CI build-time Sentry vars** (`VITE_SENTRY_ORG`, `VITE_SENTRY_PROJECT`, `SENTRY_AUTH_TOKEN`, `CI`) are configured in the CI environment (match `.env.example` keys).
2754

28-
- Secrets (`AUTH_SECRET`, AWS keys) should be set with `wrangler secret put`.
29-
- Non-secrets (`AWS_REGION`, `SES_FROM_EMAIL`, optional `SENTRY_DSN`) can be set via `vars` in `wrangler.toml` or `wrangler deploy --var KEY=value`.
30-
- Build-time Sentry vars for CI are configured in the CI environment (match `.env.example` keys).
31-
- Runtime env must be read only through `getRuntimeEnv()` inside server code.
55+
- **Domains:** staging uses the default `*.workers.dev` URL; production uses the custom domain `app.happyharmony.dev`.
56+
57+
- **Rule:** runtime env must be read only through `getRuntimeEnv()` inside server code.
3258

3359
## Validation
3460

35-
- Build-time keys are validated by `buildEnvSchema` in `src/config/validation.ts` and parsed via `parseBuildEnv` in `vite.config.ts` / `src/env.ts`. When `CI=true`, Sentry keys are required; otherwise they stay optional.
36-
- Runtime keys are validated by `getRuntimeEnv()` in `src/config/runtimeEnv.ts`; error messages list missing keys without revealing values.
61+
- **Build-time** keys are validated by the schema in `src/config/validation.ts`.
62+
- When `CI=true`, Sentry build keys are required; otherwise they remain optional.
63+
- **Runtime** keys are validated by `getRuntimeEnv()` in `src/config/runtimeEnv.ts`.
64+
- Error messages list missing keys without revealing values.
3765

38-
## Sentry defaults
66+
## Sentry (recommended defaults)
3967

40-
- Server (Workers): DSN from runtime `SENTRY_DSN` via `getRuntimeEnv()`. Sample rates: traces 0.1 / profiles 0.1 in production, 1.0 in dev/staging. Environment comes from `WORKERS_ENVIRONMENT`/`NODE_ENV`.
41-
- Client: DSN from `VITE_SENTRY_DSN` (optional). Sample rates mirror server; replays are off by default (`replaysSessionSampleRate=0`, `replaysOnErrorSampleRate=1.0`). Environment uses `import.meta.env.MODE`.
68+
- **Server (Workers):** DSN from runtime `SENTRY_DSN` via `getRuntimeEnv()`.
69+
- **Client:** use whatever TanStack Start / Sentry integration requires (avoid exposing secrets; DSN is not a secret).
70+
- **Privacy:** never include user-entered activity text/notes in logs, breadcrumbs, or error reports.
4271

4372
## Testing
4473

45-
- Playwright uses `pnpm dev` (wrangler) so e2e hits the Workers runtime on port 3000. `.dev.vars` must be present for runtime bindings.
74+
- Playwright uses `pnpm dev:vite` by default, so E2E tests do **not** hit the Workers runtime unless you change the Playwright `webServer` command.
75+
- `.dev.vars` is required only when tests exercise runtime-dependent routes and you run via `pnpm dev` (Wrangler).
76+
77+
## Deployment & domains
78+
79+
- `workers_dev` is enabled so deploys always have a `*.workers.dev` URL available (used for staging and debugging).
80+
- Staging deploys use the `*.workers.dev` URL (no custom domain).
81+
- Production is served from the **Custom Domain** `app.happyharmony.dev`, managed in the Cloudflare dashboard.
82+
- **Do not configure `route` patterns in `wrangler.jsonc`** (we intentionally keep the apex `happyharmony.dev` free for marketing later).
83+
84+
## Environment variable reference
85+
86+
### Build-time (Vite/CI)
87+
88+
| Key | Secret? | Used where | Purpose |
89+
| --------------------- | ------: | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
90+
| `CI` | No | CI environment; `vite.config.ts` | Controls CI-only behavior (e.g., enabling sourcemap upload). When `true`, Sentry build vars are required. |
91+
| `VITE_SENTRY_ORG` | No | CI environment; `vite.config.ts` | Sentry org slug for sourcemap upload configuration. |
92+
| `VITE_SENTRY_PROJECT` | No | CI environment; `vite.config.ts` | Sentry project slug for sourcemap upload configuration. |
93+
| `SENTRY_AUTH_TOKEN` | **Yes** | CI environment; `vite.config.ts` | Auth token used by the Sentry Vite plugin to upload sourcemaps. Never expose to runtime/client code. |
94+
| `VITE_SENTRY_DSN` | No | Client build-time env; `src/app/sentry.client.ts` | Client Sentry DSN for browser error reporting. |
95+
96+
### Runtime (Workers)
97+
98+
| Key | Secret? | Set where | Used where | Purpose |
99+
| ----------------------- | ------: | --------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------- |
100+
| `AUTH_SECRET` | **Yes** | Wrangler secret / Cloudflare env secret | Server runtime via `getRuntimeEnv()` | Secret used by authentication/session crypto. Must be long and random in staging/prod. |
101+
| `AWS_REGION` | No | Wrangler var / Cloudflare env var | Server runtime via `getRuntimeEnv()` | AWS region used when calling SES APIs. |
102+
| `AWS_ACCESS_KEY_ID` | **Yes** | Wrangler secret / Cloudflare env secret | Server runtime via `getRuntimeEnv()` | Access key for SES sending credentials (least-privilege IAM). |
103+
| `AWS_SECRET_ACCESS_KEY` | **Yes** | Wrangler secret / Cloudflare env secret | Server runtime via `getRuntimeEnv()` | Secret key for SES sending credentials (least-privilege IAM). |
104+
| `SES_FROM_EMAIL` | No | Wrangler var / Cloudflare env var | Server runtime via `getRuntimeEnv()` | Verified “From” address used for verification/reset emails. |
105+
| `SENTRY_DSN` | No | Wrangler var / Cloudflare env var | Server runtime via `getRuntimeEnv()` | Sentry DSN for server-side error reporting. DSN is not secret, but treat it as configuration. |
46106

47-
## Deployment
107+
### Local files (never commit)
48108

49-
- `workers_dev` is enabled for the first deploy to `*.workers.dev`. Route settings remain in `wrangler.jsonc` for attaching the custom domain afterward.
109+
| File | Purpose | Notes |
110+
| ------------ | ----------------------------------------- | ---------------------------------------------------------------------------------- |
111+
| `.dev.vars` | Local runtime bindings for `wrangler dev` | Copy from `.dev.vars.example`. Do not commit. |
112+
| `.env.local` | Optional local build-time vars | Typically only needed if you want CI-like sourcemap upload locally. Do not commit. |

vite.config.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@ export default defineConfig((configEnv) => {
1616
const isSsrBuild = Boolean(ssrBuild);
1717
const isVitest = process.env.VITEST === "true";
1818
const isPlaywright = process.env.PLAYWRIGHT_TEST === "true";
19-
const isCi = process.env.CI === "true" || process.env.CI === "1";
2019
const shouldAliasDevtools =
21-
isSsrBuild ||
22-
isVitest ||
23-
(isPlaywright && command === "serve") ||
24-
(isCi && command === "build");
20+
isSsrBuild || command === "build" || isVitest || isPlaywright;
2521
const shouldUseCloudflare = !isVitest;
2622

2723
const shouldSkipEnvLoad = process.env.SKIP_ENV_LOAD === "true";

wrangler.jsonc

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
{
2-
"$schema": "./node_modules/wrangler/config-schema.json",
3-
"name": "happy-harmony",
4-
"main": "./node_modules/@tanstack/react-start/dist/default-entry/esm/server.js",
5-
"compatibility_date": "2026-01-07",
6-
"compatibility_flags": ["nodejs_compat", "remove_nodejs_compat_eol"],
7-
"workers_dev": true,
8-
"observability": { "enabled": true },
9-
"route": {
10-
"pattern": "happyharmony.dev/*",
11-
"zone_name": "happyharmony.dev",
12-
},
13-
"env": {
14-
"staging": {
15-
"name": "happy-harmony-staging",
16-
"route": {
17-
"pattern": "staging.happyharmony.dev/*",
18-
"zone_name": "happyharmony.dev",
19-
},
20-
},
21-
},
2+
"$schema": "./node_modules/wrangler/config-schema.json",
3+
"name": "happy-harmony",
4+
"main": "./node_modules/@tanstack/react-start/dist/default-entry/esm/server.js",
5+
"compatibility_date": "2026-01-07",
6+
"compatibility_flags": ["nodejs_compat", "remove_nodejs_compat_eol"],
7+
"workers_dev": true,
8+
"observability": { "enabled": true },
9+
"env": {
10+
"staging": {
11+
"name": "happy-harmony-staging"
12+
}
13+
}
2214
}
15+
// Custom domain (app.happyharmony.dev) is attached in Cloudflare dashboard via Custom Domains.
16+
// Do not add "route" here; we want to keep apex free for "marketing page(s)".

0 commit comments

Comments
 (0)