Skip to content

Developer Environment

This document describes how developers work in this repository using VS Code Dev Containers and Docker Compose. It references files/paths rather than embedding configuration snippets.

Goals

  • Consistent, reproducible tooling across machines
  • Per-service runtime isolation (each service image defines its own Python/Node deps)
  • Fast iteration with profile-based Compose runs

Key files

  • trade_psykl.code-workspace — multi-root workspace for per-service Python venv isolation. Open this file in VS Code for proper import resolution.
  • .devcontainer/devcontainer.json — editor/tooling container (Python 3.12, Node LTS, Docker CLI). Services still run as separate containers.
  • docker-compose.yml — multi-service composition with profiles (api, engine, ui, docs, data, plus grouped web, observability, all). Docs run natively in devcontainer for live reload (port 5173); container profile is for production builds only. Engine maps host port 8001 to container 8080 to avoid conflicts.
  • .env — default Compose profiles for docker compose up (see COMPOSE_PROFILES).
  • .vscode/tasks.json — one-click tasks to start/stop stacks and run quick tests.
  • .gitattributes, .editorconfig — line endings and formatting conventions (LF-only; consistent across OSes).
  • docs/GETTING-STARTED.md, docs/devops/docker.md, docs/devops/docker-profiles.md — runtime and Docker guidance.

Developer dependencies (host)

These are required on your machine; the app services themselves still run in containers.

  • Windows 10/11 with WSL 2 enabled (or macOS/Linux). On Windows, prefer WSL 2 for performance.
  • Docker Desktop with the WSL 2 backend enabled. Turn on “WSL Integration” for your distro.
  • A WSL distribution (Ubuntu 22.04+ recommended) when on Windows.
  • Visual Studio Code with the “Dev Containers” extension.
  • Git (Git for Windows or your platform package).
  • Optional: PowerShell 7+ (pwsh) for consistent shell behavior on Windows.

Notes

  • You do not need host-level Python or Node; the devcontainer supplies the CLI tooling and each service image supplies its own runtime dependencies.
  • Ensure Docker Desktop is running before using Compose or reopening in the devcontainer.

Dev Container usage

  • Open the repo in VS Code and use "Reopen in Container". The container provides Python/Node tooling, Docker CLI access, and installs pre-commit hooks on start.
  • Important: After opening in the devcontainer, open the workspace file: File > Open Workspace from File…trade_psykl.code-workspace. This enables per-service Python import resolution (see "Multi-root workspace" section below).
  • Git identity is inherited by mounting your host ~/.gitconfig into the container.
  • Do not add service dependencies to the devcontainer. Each service image defines its own dependencies and Python/Node versions.

Devcontainer configuration details

This section explains what the devcontainer applies and why. See .devcontainer/devcontainer.json for the authoritative configuration.

  • Image

    • image: mcr.microsoft.com/devcontainers/python:3.12
    • Rationale: stable base with Python 3.12 and Debian trixie; we add Node and Docker CLI via features.
  • User

    • remoteUser: vscode
    • Rationale: non-root user for predictable file ownership and better security.
  • Features

    • ghcr.io/devcontainers/features/node:1 with { version: "lts" }
      • Provides Node LTS for UI tooling and docs tasks inside the devcontainer.
    • ghcr.io/devcontainers/features/docker-outside-of-docker:1 with { moby: false }
      • Installs Docker CE CLI and forwards the host Docker socket into the container, so you can run docker/compose inside the devcontainer. moby: false is required on Debian trixie (Moby packages were removed).
  • Lifecycle commands

    • postCreateCommand: python -m pip install --upgrade pip ruff black pre-commit && bash scripts/dev/bootstrap_venvs.sh
      • Tools-only install (pip/ruff/black/pre-commit) and per-service virtualenv bootstrap for editor import resolution.
      • We intentionally do not install per-service deps in the devcontainer; those live in service images.
      • The bootstrap script creates local .venv directories under src/api and src/engine and installs their requirements.txt so Pylance can resolve imports without polluting the devcontainer.
    • postStartCommand: pre-commit install || true; ... ln -s host-home/.gitconfig
      • Ensures Git hooks are active on every start and symlinks host Git config if present (idempotent and safe).
  • Environment

    • containerEnv.LOCAL_WORKSPACE_FOLDER = ${localWorkspaceFolder}
      • Lets docker compose run inside the devcontainer with host-safe bind mounts that reference the host path.
    • containerEnv.PIP_DISABLE_PIP_VERSION_CHECK = 1
      • Silences pip update notices to keep the terminal clean.
    • remoteEnv.PYTHONUNBUFFERED = 1
      • Ensures Python logs flush immediately (useful for streaming logs).
  • Mounts

    • mounts: [ source=${localEnv:USERPROFILE}, target=/home/vscode/host-home, type=bind ]
      • Exposes your Windows user profile into the container at /home/vscode/host-home. On start, we symlink .gitconfig from there if it exists.
  • VS Code customizations

    • customizations.vscode.settings
      • Python: interpreter path default, Pylance typeCheckingMode: basic, black as formatter, formatOnSave: true.
      • Testing: python.testing.pytestEnabled: true, unittestEnabled: false, and default pytest args -q.
    • customizations.vscode.extensions
      • Curated set for Python, Docker, Markdown, GitHub, Jupyter, YAML, etc. Extensions run in the devcontainer so tools match the container’s environment.
  • Ports

    • forwardPorts: [8000, 5173, 8080, 27017]
      • Convenience forwards for API, UI dev server, native VitePress (5173), and MongoDB.
      • VitePress native dev server (5173) for live reload; container (8080) for production builds.

Rebuild vs. Restart

  • Rebuild required when you change: image, features, mounts, containerEnv, forwardPorts, or VS Code customizations.
  • Restart is enough when you change: postStartCommand only.
  • No rebuild for: .vscode/tasks.json, .env, docs, and application source (unless features/image changed).

Security and privacy

  • The devcontainer mounts your host user profile read-only for Git identity discovery. Only a symlink to .gitconfig is created if it exists; no secrets are copied. Remove the mount if you prefer to set Git identity inside the container.

Running services

  • Use Compose profiles to run only what you need. The default stack (web,observability) is set in .env.
  • Recommended: use VS Code tasks under .vscode/tasks.json to start/stop stacks and follow logs.
  • For details on profiles and combinations, see docs/GETTING-STARTED.md and docs/devops/docker-profiles.md.

Documentation development workflow

VitePress runs natively in the devcontainer for development with live reload. The docs container serves pre-built static files for production.

Development (live reload):

bash
cd /workspaces/trade_psykl/docs
npm install  # First time only
npm run docs:dev -- --host 0.0.0.0

Or use VS Code task: Docs: Dev Server (Native)

Access at: http://localhost:8080

  • ✅ Live reload - changes auto-update in browser
  • ✅ Fast startup
  • ✅ File watching via polling (WSL2 compatible)

Production container (static build):

The docs container builds static files and serves them with VitePress preview server:

bash
docker compose build docs        # Builds static files during image creation
docker compose --profile docs up -d

Access at: http://localhost:8080

Key differences:

AspectNative DevContainer Prod
Use caseDocumentation editingDeployment, CI/CD
BuildOn-demand by VitePre-built during docker build
Live reload✅ Yes❌ No (static files)
PerformanceFast iterationFast serving
RequirementsNode.js in devcontainerDocker only

Why native for dev?

Docker bind mounts from /workspaces/ in WSL2 devcontainers don't reliably propagate file system events. Running VitePress natively with polling-based file watching solves this and provides full HMR (Hot Module Reload).

Multi-root workspace

TL;DR: Open trade_psykl.code-workspace instead of the folder root for proper Python import resolution.

Why multi-root?

Each service (API, Engine) has its own Python dependencies isolated in a local .venv. Opening the repo as a single folder causes Pylance to use one interpreter for all files, resulting in import errors in service code (e.g., FastAPI not found in Engine).

How it works

  1. trade_psykl.code-workspace defines three workspace roots:

    • Root folder (📁 trade_psykl (root)) — docs, docker-compose, infrastructure
    • API folder (🔌 API Service) — src/api with its own .vscode/settings.json.venv
    • Engine folder (⚙️ Engine Service) — src/engine with its own .vscode/settings.json.venv
  2. On devcontainer build, postCreateCommand runs scripts/dev/bootstrap_venvs.sh:

    • Creates src/api/.venv and installs src/api/requirements.txt
    • Creates src/engine/.venv and installs src/engine/requirements.txt
    • These venvs are for editor import resolution only (services run in containers with their own dependencies)
  3. Each service folder's .vscode/settings.json points python.defaultInterpreterPath to its local .venv/bin/python

  4. Pylance resolves imports per workspace root using the corresponding venv

Opening the workspace

  • First time: File > Open Workspace from File…trade_psykl.code-workspace
  • Later: Recent workspaces list shows trade_psykl (Workspace) — select that
  • Indicator: VS Code title bar shows "trade_psykl (Workspace)" instead of just the folder name

Without the workspace

If you open the repo as a regular folder, you'll see import errors in service code:

  • Import "fastapi" could not be resolved
  • Import "opentelemetry" could not be resolved

Solution: Open the workspace file.

Re-bootstrapping venvs

If imports break after dependency changes:

  • Run task: Terminal > Run Task… > Dev: Bootstrap venvs
  • Or rebuild the devcontainer (slower but comprehensive)

Future services

When adding new Python services (e.g., src/worker):

  1. Add folder to trade_psykl.code-workspace folders array
  2. Create src/worker/.vscode/settings.json with python.defaultInterpreterPath: "${workspaceFolder}/.venv/bin/python"
  3. Update scripts/dev/bootstrap_venvs.sh to create venv and install requirements
  4. Rebuild devcontainer or run bootstrap task

Testing

  • Preferred: run tests inside the relevant service container to match its dependencies (see the test tasks in .vscode/tasks.json).
  • Optional: the devcontainer enables pytest discovery in VS Code. If you want Test Explorer to execute tests locally in the devcontainer, ensure the required test dependencies are available to that environment or use container-executed test tasks.

Pre-commit hooks

  • Configuration lives in .pre-commit-config.yaml.
  • Hooks include common hygiene and Python tooling:
    • trailing whitespace, end-of-file fixer, mixed-line-ending (forced to LF)
    • check-merge-conflict, check-yaml, check-json, check-added-large-files (500 KB)
    • black (Python 3.12), ruff with --fix and --exit-non-zero-on-fix
  • Usage inside the devcontainer:
    • On first setup, run pre-commit run --all-files to format/lint the repo.
    • On every commit, hooks run automatically. If committing on the host, ensure pre-commit is installed there (e.g., via pipx install pre-commit) or commit from inside the devcontainer. As a last resort, use --no-verify to bypass hooks for that commit.

Line endings and formatting

  • .gitattributes enforces LF line endings; .editorconfig configures editors to write LF and a consistent style.
  • If you encounter line-ending warnings, normalize the working tree using git add --renormalize . (see Git history for the normalization commit).

Windows notes

  • For best bind-mount performance, use Docker Desktop with the WSL 2 backend and keep the repository under WSL. The composition uses host-safe bind mounts so it works from both host and devcontainer.

Next steps

  • The repository includes .pre-commit-config.yaml; the devcontainer installs and activates pre-commit hooks automatically.
  • If you later want a production-like base docker-compose.yml, introduce a docker-compose.dev.yml override for dev-only commands/mounts and run with both files.

Documentation generated with VitePress