CONTRIBUTING.md
Rama newjitsu · réplica de lectura del repositorio original.
# 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. `<repo>/.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 <id>`). 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.