Files
zpan/CONTRIBUTING.md
T
Jasper Van b92df828ab ci: parallelize and isolate test suites (#556)
* ci: parallelize and isolate test suites

* ci: avoid unavailable Playwright video runtime

* ci: shard coverage and cache docker smoke

* ci: balance Playwright shards by test

* ci: smoke test the CLI container

* ci: enforce merged coverage thresholds

* ci: ratchet canonical coverage baseline

* ci: make coverage ratchet lossless

* ci: organize parallel gates by responsibility

* perf(ci): reduce total runner time

* perf(ci): balance runner cost and latency

* perf(ci): suppress passing test logs

* fix(test): make coverage sorting proof deterministic

* perf(docker): exclude test-only build inputs

* perf(ci): scope Docker smokes to packaging changes

* refactor(test): enforce fast test boundaries

* test: isolate coverage ownership

* perf(test): run backend projects concurrently

* perf(ci): separate test layers by runtime

* perf(test): separate integration boundaries

* perf(ci): prioritize test runners

* docs(ci): clarify package scheduling

* test: restore shared Cloudflare mocks

* fix(preview): isolate Cloudflare E2E build config

* fix(auth): bind preview sessions to request origin

* revert: remove ineffective preview auth workaround

* fix(auth): stop signing JWTs on session reads
2026-08-05 15:01:12 -04:00

8.0 KiB

Contributing

By participating in this project, you agree to abide by our Code of Conduct.

Setup

Prerequisites: Node.js 24+ (managed by Volta)

git clone git@github.com:saltbo/zpan.git
cd zpan
pnpm install

Development

pnpm dev              # CF Workers mode with HMR (default, uses staging D1)
pnpm dev:node         # Node.js mode with HMR (SQLite, reads .dev.vars)

Quality Gates

Every commit and PR must pass these checks. Husky enforces them on pre-commit.

pnpm lint             # Biome — lint + format check
pnpm typecheck        # TypeScript strict mode
pnpm test                 # Unit + integration tests (Node runtime, 90% coverage gate)
pnpm test:cf          # Integration tests (Cloudflare Workers runtime)
pnpm e2e              # Playwright E2E tests

Test boundaries

  • Backend unit tests use *.test.ts, run in Node, and must not create a database-backed application.
  • Frontend unit and component tests use *.test.ts(x) and run in jsdom.
  • Backend repository, HTTP, and application integration tests use *.integration.test.ts; CI runs HTTP/application and datastore/adapter boundaries independently.
  • *.cf-test.ts is reserved for behavior that specifically needs D1, Workers bindings, or the Workers runtime.
  • Pull requests run only @critical browser journeys against the local Cloudflare runtime. The complete multi-viewport browser suite runs nightly.
  • Tests must replace S3 and ZPan Cloud with local fakes; CI must not depend on staging services or credentials.

Adding a Feature

  1. Write code in the relevant directory (server/, src/, shared/)
  2. Write tests — co-locate with source as *.test.ts (Node) or *.cf-test.ts (CF Workers)
  3. Run checkspnpm lint && pnpm typecheck && pnpm test && pnpm test:cf
  4. Coverage — new code must maintain 90%+ line coverage on server/
  5. Commit — use Conventional Commits (feat:, fix:, docs:, etc.)
  6. PR — target the main branch
  7. Preview verification — every PR must be verified in the preview environment (see below)

Preview Verification

Every PR that touches UI or API behavior must be verified in the Cloudflare Workers preview environment before merging. The verification report must be posted as a PR comment — a PR without a verification comment cannot be merged.

Cloudflare Workers automatically deploys each PR to a preview URL (posted as a PR comment).

Before merging, the reviewer must verify in the preview environment and post a PR comment with:

  • Screenshots proving the feature works (golden path + edge cases)
  • What was tested (e.g. "Switched theme to dark, changed language to Chinese, verified password mismatch error")
  • Verdict — approve or request changes

A code-review-only approval (reading the diff without visiting the preview) is not sufficient to merge.

Preview environment details

  • All PRs share one staging D1 database (zpan-db-staging) — data persists across deployments
  • A dev storage backend is pre-configured, so file upload works out of the box
  • If you need a clean state, coordinate with maintainers

Staging test accounts

A shared test account is available on the staging database for preview verification:

Field Value
Email reviewer@zpan.dev
Password zpan-staging-reviewer-2026

Use this account for UI regression testing in preview deployments. Do not change the password — other contributors depend on it.

For admin feature testing, use the dedicated non-production preview admin account:

Field Value
Email admin@zpan.space
Password Private maintainer DEV_ADMIN_PASSWORD value

Use this account only in the staging/preview environment. Do not commit, post, or use the password for production.

If the staging admin account is missing, demoted, or the password stops working, a maintainer can repair it without touching production:

pnpm seed:preview-admin

The command reads DEV_ADMIN_PASSWORD from the shell environment or the gitignored local .dev.vars file, then targets zpan-db-staging with --env staging --remote and upserts only admin@zpan.space. To rotate the shared preview password intentionally, run the same command with the new non-production DEV_ADMIN_PASSWORD value and update the private maintainer credential source in the same change.

Database Migrations

Schema is defined in server/db/schema.ts and server/db/auth-schema.ts.

pnpm db:generate                            # Generate migration SQL after schema changes
pnpm db:migrate                             # Apply migrations (Node/SQLite)
pnpm db:migrate:d1                          # Apply migrations (D1 local)
wrangler d1 migrations apply zpan-db --remote  # Apply migrations (D1 production)

To reset local databases with seed data (admin user + dev storage):

pnpm db:reset                # Reset Node database (zpan.db)
pnpm db:reset:d1             # Reset D1 local database (.wrangler)

Migration files live in migrations/ at project root. Always commit them.

Storage usage backfill

After applying the migration that creates storage_usage_breakdowns, run the storage usage backfill once. The command is a dry run unless --apply is present.

pnpm storage:backfill -- --d1 zpan-db --remote
pnpm storage:backfill -- --d1 zpan-db --remote --apply

pnpm storage:backfill -- --sqlite zpan.db
pnpm storage:backfill -- --sqlite zpan.db --apply

The backfill recalculates all eight storage categories from matters and image_hostings. It is an operator command, not part of the application runtime or deployment lifecycle.

Storage enabled/status backfill

After applying migrations 0066 through 0069, convert legacy storage status values into the enabled flag and the unknown/healthy/unhealthy health model:

pnpm storage-status:backfill -- --d1 zpan-db --remote
pnpm storage-status:backfill -- --d1 zpan-db --remote --apply

pnpm storage-status:backfill -- --sqlite zpan.db
pnpm storage-status:backfill -- --sqlite zpan.db --apply

Run this once before deploying the application version that reads health status.

Turso (libSQL) migrate path

When deploying the Node/Docker image against a Turso (libSQL) database, set TURSO_DATABASE_URL (and TURSO_AUTH_TOKEN for remote URLs) before running db:migrate. drizzle.config.ts detects the env var and switches to the turso dialect automatically:

TURSO_DATABASE_URL=libsql://your-db.turso.io \
TURSO_AUTH_TOKEN=your-token \
pnpm db:migrate

For local libSQL files the token can be omitted:

TURSO_DATABASE_URL=file:./zpan.db pnpm db:migrate

Migrations run automatically at Docker container startup when TURSO_DATABASE_URL is set. See docs/deploy/docker.md for the full Docker + Turso setup.

Deployment

Primary target is Cloudflare Workers. Node.js (Docker) is the backup runtime.

pnpm build            # Build frontend to dist/
pnpm deploy           # Build + deploy to Cloudflare Workers

Project Structure

zpan/
├── server/               # Hono API (routes, middleware, auth, platform abstraction)
├── src/                  # React frontend (TanStack Router, shadcn/ui)
├── shared/               # Shared types, Zod schemas, constants
├── workers/              # Cloudflare Workers entry
├── migrations/           # D1/SQLite migrations (drizzle-kit generated, wrangler managed)
├── wrangler.toml         # Cloudflare Workers config
└── biome.json            # Lint + format config

Financial Contributions

We welcome financial contributions on our Open Collective.

Contributors

Thank you to all the people who have already contributed to ZPan!