# Repository Layout This is a monorepo spanning Go and TypeScript. ``` bulker/ Go services bulkerapp/ Bulker — warehouse ingestion service ingest/ HTTP ingest endpoint sync-controller/ Connector sync orchestration bulkerlib/ Core ingestion library connectors/ Warehouse connectors (ClickHouse, BigQuery, Redshift, Snowflake, S3, GCS, …) services/ rotor/ Event routing, transformation, function execution webapps/ console/ Admin UI (Next.js), Management API, MCP server libs/ jitsu-js/ Browser + Node.js SDK (@jitsu/js) jitsu-react/ React bindings (@jitsu/jitsu-react) functions/ Functions runtime (@jitsu/functions-lib) juava/ Shared TypeScript utilities cli/jitsu-cli/ Developer CLI (jitsu-cli on npm) types/protocols/ Shared TypeScript protocols (@jitsu/protocols) helm/ Helm chart docker/ Docker Compose setup (deprecated) ``` # Prerequisites - `node: >=22` - `npx` - `pnpm: >= 10` - `docker: >= 19.03.0` - `go: 1.26` — for the Go services; the repo uses Go workspaces via `go.work` # Commands - `pnpm install` - Install dependencies - `pnpm codegen` - Generate the Prisma client + zod schemas. Required once after a fresh checkout (or a new worktree) before anything else will build - `pnpm build` - Build the project - `pnpm build:turbo` - Same, orchestrated by Turbo - `pnpm format` - Apply prettier to the project, only to changed files - `pnpm format:check` - Check if prettier needs to be applied, check only changed files - `pnpm format:check:all` - Check if prettier needs to be applied. Check all files - `pnpm format:all` - Same as `pnpm format`, but check all files, regardless of changes - `pnpm typecheck` - Run typecheck - `pnpm typecheck:turbo` - Same, orchestrated by Turbo - `pnpm lint` - Run linter - `pnpm test` - Run tests - `pnpm console:dev` - Run just the console For the Go services, inside `bulker/`: ```bash go build ./... go test ./... ``` # Local Dev Env Run * `docker compose -f ./docker/docker-compose.yml up --force-recreate` to start all dependencies required to run Jitsu. * `docker compose -f ./docker/docker-compose.yml up --profile jitsu-services-dev --force-recreate` - to run dependencies + all Jitsu services in a hot reload mode, see `docker/README.md` # Environment variables Every node process spawned by a pnpm script auto-loads two layered `.env.local` files (later wins; existing `process.env` always wins over both, missing files skipped silently): 1. `~/.jitsu/.env.local` — shared across all worktrees of all branches (Firebase, Stripe, OIDC, GitHub OAuth — anything that doesn't change per branch). 2. `/.env.local` — per-worktree (`DATABASE_URL`, `NEXTAUTH_URL`, `AUTH_COOKIE_DOMAIN`, anything that should differ between two worktrees of two PRs). **No wrapper.** `node --inspect script.js` and your debugger attach to the script's own process directly — there's no `dotenv-cli` parent in the tree. `.env.example` documents the variables the apps expect. Runtime defaults belong in code (`process.env.FOO ?? "default"`), not in a tracked `.env`. ## How it works The root [`.npmrc`](.npmrc) sets `node-options=--require=env-preload`, so pnpm exports `NODE_OPTIONS=--require=...` for every node process it spawns from a script. The preload ([`env-preload/preload-env.cjs`](env-preload/preload-env.cjs)) loads `~/.jitsu/.env.local`, then walks up from `process.cwd()` to find `pnpm-workspace.yaml` and load the repo-root `.env.local`. (Why a preload instead of `--env-file-if-exists`: Node disallows `--env-file*` in `NODE_OPTIONS` for security; `--require` is allowed.) ## Adding to the shared layer ```bash mkdir -p ~/.jitsu && chmod 700 ~/.jitsu touch ~/.jitsu/.env.local && chmod 600 ~/.jitsu/.env.local echo 'STRIPE_KEY=sk_live_xxx' >> ~/.jitsu/.env.local ``` # Testing `pnpm test` runs every package's tests. Most are plain vitest/jest suites; the console has a two-project setup worth knowing about. ## Console tests (`webapps/console`) Two vitest projects, split by what they need: - **unit** (`__tests__/unit/`) — pure tests, no external dependencies. Instant, run anywhere. - **integration** (`__tests__/integration/`) — service tests against **real Postgres and ClickHouse** started via testcontainers (Docker required), with MSW intercepting all outbound HTTP. No `vi.mock` anywhere: SQL runs against real databases, access checks and audit logging hit the same database the service writes to. ```bash cd webapps/console pnpm test # both projects (starts containers) pnpm exec vitest run --project unit # pure tests only, no Docker pnpm exec vitest run __tests__/integration/sync-service.test.ts # one file ``` How the harness works (`__tests__/integration/support/`): a global setup boots one Postgres (`postgres:18-alpine`, matching prod) and one ClickHouse (`clickhouse/clickhouse-server:25.4-alpine`) container per run and pushes the prisma schema into a sealed template database. Each test file runs in its own process (vitest forks + isolate) and clones the template into a private database (~150ms), so files are fully isolated and can run in parallel. Baseline env is set before the test file's imports, which is what binds the `db.prisma()` / `pgPool` / `clickhouse` singletons to the per-file databases. Every outbound HTTP request must have an MSW handler (`server.use(...)`) or the test fails loudly; requests to the ClickHouse container pass through. ### Container reuse (optional, local only) A cold run pays for container boot + `prisma db push` (~10–30s depending on the machine, more if images need pulling). To keep the containers running between runs: ```bash CONSOLE_TEST_CONTAINERS_REUSE=1 pnpm test ``` Subsequent runs connect in about a second. The template database is keyed by a hash of `schema.prisma`, so schema changes rebuild it automatically, and leftover per-file databases from previous runs are swept at startup. **Trade-off: the two containers keep running until you remove them** (`docker ps` → `docker rm -f `). Don't set this in CI. Notes: - `vitest --watch` is supported for the unit project; for integration prefer `vitest run` (re-runs re-clone databases in new forks, containers stay up for the session). - Per-test env overrides use `vi.stubEnv` (auto-restored). Baseline env lives in `__tests__/integration/support/setup.ts`. - `__tests__/integration/support/harness.ts` exposes `deps()` (the real singletons) and `seedWorkspace()`; other fixtures are plain `prisma.create` / `clickhouse.insert` calls inline in the tests. # Development Workflow ## Development Branch The default development branch is `newjitsu`. ## Common Principles **Branch naming:** Use a type prefix — `feat/`, `fix/`, `chore/`. Example: `feat/workspace-oidc`. **Merging policy:** When working on a feature branch, never merge the default branch into it — always rebase your branch onto the latest default branch. When merging a PR into the default branch, either "Create a merge commit" (the default) or "Rebase and merge" is fine. Squash merge stays off; if you want to squash overly granular commits, do it locally before opening the PR. **Commit style:** [Conventional commits](https://www.conventionalcommits.org/) — `type(scope): description`. Common types: `fix`, `feat`, `chore`, `refactor`, `ci`. Examples: `fix(rotor): enable DNS caching for undici pools`, `feat(console): add workspace OIDC configuration`. ## PRs vs Direct Commits Trivial changes, bug fixes, and config updates go directly to `newjitsu`. Larger or riskier changes use pull requests. The engineer decides based on complexity and risk. ## CI Checks [lint.yml](.github/workflows/lint.yml) runs on every push and PR: - Prettier format check, TypeScript typecheck, ESLint - Jest unit tests - Playwright E2E tests (frontend changes only) - Go integration tests against real cloud warehouses (AWS S3, BigQuery, Redshift, Snowflake) ## AI Review [ai-review.yml](.github/workflows/ai-review.yml) runs on every PR and on direct pushes to `newjitsu`. It uses OpenAI Codex to check for bugs, security issues, and correctness problems — style nitpicks are skipped. For PRs it posts a review via a GitHub App. For direct commits it posts a commit comment. ## Release There are two independent release pipelines with separate versioning. **Services & CLI tools** — Docker images for backend services (console, rotor, functions-server, bulker, ingest, and others) and NPM packages (`jitsu-cli`, `@jitsu/functions-lib`). Managed by [services.yaml](.github/workflows/services.yaml). Base version in [.services.version.json](.services.version.json). **Client libraries** — NPM packages `@jitsu/js`, `@jitsu/jitsu-react`, `@jitsu/protocols`. Managed by [client-libraries.yaml](.github/workflows/client-libraries.yaml). Base version in [.jsclient.version.json](.jsclient.version.json). Each pipeline publishes to three channels determined by the branch: | Pipeline | `newjitsu` | `stable-services` | `stable-jsclient` | Any other branch | |------------------|-------------------------|--------------------|--------------------|-------------------------------------------| | Services & CLI | beta — `2.14.1-beta.N` | stable — `2.14.1` | — | canary — `2.14.1-canary.20260416.abc1234` | | Client libraries | beta — `1.11.0-beta.N` | — | stable — `1.11.0` | canary — `1.11.0-canary.20260416.abc1234` | - **Stable** — `X.Y.Z` - **Beta** — `X.Y.Z-beta.N`, where `N` is auto-incremented based on existing git tags - **Canary** — `X.Y.Z-canary.YYYYMMDD.shortsha` — no git tag or GitHub release created - The base version `X.Y` is defined in `.services.version.json` or `.jsclient.version.json`; `Z` is the patch number, incremented per release Builds are **not** produced on every merge. A push to `newjitsu` publishes only when a version file (`.services.version.json` or `.jsclient.version.json`) is bumped in that push: [lint.yml](.github/workflows/lint.yml) waits for tests to pass, then dispatches [publish.yml](.github/workflows/publish.yml), which builds the affected stack(s) — stable if the matching version file changed, otherwise beta. Commits that don't bump a version file publish nothing; trigger off-cycle canary/beta builds with a manual `publish.yml` dispatch. On a successful beta or stable release, a GitHub release is created and the infra repo is notified via webhook to update deployment configs.