Skip to content

Development Guide

Local development setup for the web app, data layer and judge worker with Docker, tests and CI.

After ./dev-deploy.sh web, use this page to work on individual services.

ToolVersionFor
Docker20.10+Compose stacks
Go1.21+Data layer
Node / Bun18+Web
Python3.10+Judge + Tavern tests
GitanyPRs
NextJudge/
├── compose/ # docker-compose.*.yml
├── scripts/ # run-e2e-tests.sh, run-data-layer-tests.sh, start-e2e-stack.sh, …
├── src/
│ ├── data-layer/src/ # Go handlers (users.go, problems.go, …)
│ ├── judge/ # languages.toml, nsjail, app.py
│ ├── web/src/app/ # Next.js routes
│ ├── cli/ # nextjudge command
│ └── docs/ # this site
├── deploy.sh # prod-ish local stack
├── dev-deploy.sh # hot reload + SEED_DATA
└── fully-reset.sh # wipe local data and volumes
TaskStart here
API bug / new endpointsrc/data-layer/src/*.go, then tests/test_data_layer.tavern.yaml
Wrong verdict / TLEsrc/judge/src/, languages.toml
UI / editorsrc/web/src/app/, src/web/src/components/
Server state / pollingsrc/web/src/hooks/queries/, src/web/src/providers/query-provider.tsx
Editor UI statesrc/web/src/lib/stores/editor-store.ts
Problem form validationsrc/web/src/lib/schemas/problem-form.ts
Types shared with APIsrc/web/src/lib/types.ts, src/web/src/lib/api.ts
Schema changesrc/data-layer/src/models.go, maybe schema_updates.sql

Web (Playwright — isolated local docker stack, not production):

Terminal window
# Smoke tests only (no judge build) — matches most CI web PRs
E2E_WITH_JUDGE=0 ./scripts/run-e2e-tests.sh
# Full suite including code run/submit (needs judge image)
E2E_WITH_JUDGE=1 ./scripts/run-e2e-tests.sh
# Pull a prebuilt judge image instead of building locally (~3 min saved)
E2E_WITH_JUDGE=1 E2E_JUDGE_USE_PULL=1 ./scripts/run-e2e-tests.sh
# Local iteration — start once, rerun only what you changed
./scripts/start-e2e-stack.sh
./scripts/run-e2e-playwright.sh --grep-invert @judge
E2E_WITH_JUDGE=1 ./scripts/start-e2e-stack.sh
./scripts/run-e2e-playwright.sh --grep @judge
./scripts/stop-e2e-stack.sh
# Or from src/web when the stack is already up:
PLAYWRIGHT_BASE_URL=http://127.0.0.1:8080 npm run test:e2e:smoke
PLAYWRIGHT_BASE_URL=http://127.0.0.1:8080 npm run test:e2e:judge

Constants and stack config live in src/web/e2e/ (constants.ts, test-stack.config.sh, docker-compose.yml). The @judge tag marks specs that need the judge worker (platform.spec.ts).

Data layer:

Terminal window
./scripts/run-data-layer-tests.sh
# or manually:
cd src/data-layer
pip install -r tests/requirements.txt
pytest tests/ -p no:warnings

Tavern API tests hit an isolated docker stack on port 5050 (tests/constants.py, tests/test_data_layer.tavern.yaml). Configure the host with TAVERN_HOST or the default in constants.py.

Judge:

Terminal window
cd src/judge && python -m pytest tests/
# or ./tests.sh

Path-filtered jobs in .github/workflows/ci.yml:

Change inRuns
src/web/**lint, Playwright smoke (no judge), Playwright judge E2E (pull prebuilt image), Docker build
src/data-layer/**Go unit tests, Tavern API tests (local stack), Docker image build
src/judge/**Judge tests (cached image build), judge image build for Playwright @judge specs
src/docs/**Docs build

Changes confined to one service usually trigger only that job.

  1. Handler in the right *.go file
  2. Route registration in the same file’s add*Routes
  3. AuthRequired, AdminRequired, or AtLeastJudgeRequired
  4. Tavern stage in tests/
  5. API reference update

See Judge service: Add a language. Rebuild basejudge:dev or the production target after Dockerfile changes.

Edit models.go. AutoMigrate runs on the next data layer start. For destructive changes, add SQL to schema_updates.sql and test with ./fully-reset.sh locally before production.

ServiceTry
Data layergo run src/main.go -d -p 5000
Judgedocker logs $(docker ps -qf name=judge)
WebDevTools + terminal running npm run dev
Postgresdocker exec -it $(docker ps -qf name=postgres) psql -U postgres nextjudge
RabbitMQhttp://localhost:15672 (creds from .env.dev)

Env sources: config.go, judge app.py, web .env.example.

Go: gofmt, early returns. TypeScript: strict, no any. Python: PEP 8, type hints on new code.

Branch from main, test locally, open a PR. See CONTRIBUTING.md.

Build all images: docker buildx bake -f docker-bake.hcl