Local development
Prerequisites, install, environment, database, running apps, and testing the albertonline.org monorepo locally.
Prerequisites
- Node.js 20+
- pnpm 11+ (the repo pins
pnpm@11.5.2viapackageManager) - PostgreSQL — for any database-backed feature
pnpm lint type-strips the TypeScript oxlint plugin at import time, which is on-by-default only
in Node ≥ 22.18. On older Node the lint step fails with Unknown file extension ".ts". Use a
recent Node 22 for the full gate.
Install
pnpm installCreate env files from the examples, then set at least DATABASE_URL:
cp apps/web/.env.example apps/web/.env
cp apps/native/.env.example apps/native/.env
# if you use them locally:
cp packages/cms/.env.example packages/cms/.env
cp packages/internationalization/.env.example packages/internationalization/.env@repo/analytics validates its env with no skipValidation, so
NEXT_PUBLIC_POSTHOG_KEY and NEXT_PUBLIC_POSTHOG_HOST must be set (dev
placeholders are fine) or every page 500s. SUPABASE_JWT_SECRET is also
required. SKIP_ENV_VALIDATION=1 bypasses the app-level schemas but not the
analytics one.
Database
pnpm db:push # push schema directly (dev)
pnpm db:studio # open Drizzle Studio
pnpm db:generate # generate a migration from schema changes
pnpm db:migrate # apply migrationsSchema lives in packages/db/src/schema/; RLS policies in
packages/db/src/rls.ts. All DB commands run from the repo root and target
@repo/db.
Running apps
pnpm dev # every app, via Turbo (kills known dev ports first)
pnpm dev:web # web only
pnpm dev:native # the Expo app
pnpm -F <pkg> dev # any single app by its package namepnpm dev runs scripts/kill-dev-ports.mjs first, so a stale process on a dev
port won't block startup.
Dev against the prod Supabase pooler
pnpm dev:web:local-prod boots the web app on :3001 against the prod
Supavisor pooler (DATABASE_URL from the git-ignored apps/web/.env.local)
while overriding the auth origins so a localhost browser is accepted
(BETTER_AUTH_URL / CORS_ORIGIN / NEXT_PUBLIC_URL →
http://localhost:3001, plus AUTH_DISABLE_POLAR_CUSTOMER_CREATE=true). The
script scripts/dev-web-local-prod.mjs bundles these — don't assemble them by
hand, and never point dev at the dead direct host db.<ref>.supabase.co.
Dev ports
| Port | App |
|---|---|
3000 | it-f-store / portal |
3001 | web |
3002 | juicy-shop |
3003 | remotion |
3004 | blog |
3005 | market-web |
3009 | docs (this site) |
3010 | cvm |
3030 | lila-server |
3031 | lila-web |
3032 | cvm-server |
6006 | storybook |
8081 | native (Expo) |
Testing
Vitest is the test runner (node + browser modes). Naming:
- Unit —
*.unit.test.ts/*.unit.spec.ts(node) - Component —
*.test.tsx/*.spec.tsx(browser, Playwright chromium) - E2E —
tests/e2e/*.e2e.test.ts - Visual —
*.visual.test.ts
pnpm test # affected suites, via Turbo
pnpm -F <pkg> exec vitest run # one package
pnpm -F <pkg> exec vitest run --coverageNew or changed production code must keep SonarQube "Coverage on New Code" at ≥ 80% — write the tests in the same change (test-first), covering the error / guard branches, not just the happy path.
Before you push
Run the same gate CI runs — see CI/CD. In short:
pnpm ci:precheck # lint + types + format + boundaries + doc-versions + fallowThe Husky pre-push hook runs ci:precheck plus tests and an affected build.
Never bypass it with --no-verify.