# Dev Containers

Prism provides CLI-first dev containers for isolated, reproducible development environments.

## Overview

Dev containers allow you to:

- Start development instantly in a fully configured environment
- Work on multiple projects simultaneously with isolated environments
- Persist your work across container restarts with named volumes
- Access services through Traefik reverse proxy

## Quick Start

```bash
# Start a dev container from a repository
prisme devcontainer start https://github.com/user/repo.git

# Open a shell in the container
prisme devcontainer shell repo
```

## Commands

### Start a Container

Create and start a new dev container:

```bash
# Basic usage
prisme devcontainer start https://github.com/user/repo.git

# With custom name
prisme devcontainer start https://github.com/user/repo.git --name myproject

# Specific branch
prisme devcontainer start https://github.com/user/repo.git --branch develop

# Skip automatic prisme generate
prisme devcontainer start https://github.com/user/repo.git --no-generate
```

If you're in a git repository, you can omit the URL:

```bash
cd my-project
prisme devcontainer start
```

### Shell Access

Open an interactive shell in a container:

```bash
# As developer user (default)
prisme devcontainer shell myproject

# As root (for system-level operations)
prisme devcontainer shell myproject --root
```

### List Containers

View all dev containers:

```bash
prisme devcontainer list
```

Output shows:
- Container name
- Status (running/stopped)
- Image version
- Creation time

### Stop a Container

Stop a running container (preserves data):

```bash
prisme devcontainer stop myproject
```

### Restart a Container

Restart a container:

```bash
prisme devcontainer restart myproject
```

### Destroy a Container

Remove a container:

```bash
# Remove container only
prisme devcontainer destroy myproject

# Remove container and all volumes (workspace, deps, database)
prisme devcontainer destroy myproject --volumes
```

### Build Custom Image

Build a local dev container image:

```bash
# Build with defaults
prisme devcontainer build-image

# Custom tag
prisme devcontainer build-image --tag v1.0.0

# Custom Python/Node versions
prisme devcontainer build-image --python 3.12 --node 20

# Build and push to registry
prisme devcontainer build-image --push
```

## What's Included

The dev container image includes:

- **Python 3.13** with `uv` package manager
- **Node.js 22** with `pnpm`
- **PostgreSQL client** for database operations
- **Git, curl, jq, vim** and other utilities

## Volume Persistence

Each container uses named volumes for persistent data:

| Volume | Purpose | Path |
|--------|---------|------|
| `{name}-persist` | Dependencies (venv + node_modules) | `/persist` |
| `{name}-pgdata` | Database data | (PostgreSQL) |

The persist volume uses symlinks to map dependencies back into the workspace:
- `/persist/venv` → `/workspace/.venv`
- `/persist/node_modules` → `/workspace/{frontend_path}/node_modules`

Data persists across container restarts. Use `--volumes` with destroy to remove.

## Traefik Integration

Containers automatically integrate with Prism's Traefik proxy:

| Service | URL |
|---------|-----|
| Frontend | `http://{name}.localhost` |
| API | `http://{name}.localhost/api` |

Make sure the Prism proxy is running:

```bash
prisme dev --docker  # Starts proxy automatically
```

## Workflow Example

1. **Start Development**
   ```bash
   prisme devcontainer start https://github.com/myorg/myproject.git
   prisme devcontainer shell myproject
   ```

2. **Inside Container**
   ```bash
   uv sync
   uv run pytest
   ```

3. **Access Services**
   - Frontend: http://myproject.localhost
   - API: http://myproject.localhost/api

4. **End Session**
   ```bash
   exit  # Leave container (it keeps running)
   prisme devcontainer stop myproject  # Stop when done
   ```

## Comparison with VS Code Dev Containers

| Feature | Prism Dev Containers | VS Code Dev Containers |
|---------|---------------------|----------------------|
| Primary Interface | CLI | VS Code |
| IDE Required | No | Yes |
| Pre-installed Tools | uv, pnpm, prisme CLI | Varies |
| Multiple Projects | Named volumes | Per-project |
| Traefik Integration | Automatic | Manual |

## Quick Reference

```bash
prisme devcontainer up --build       # rebuild from scratch
prisme devcontainer test             # run all tests
prisme devcontainer exec 'uv run prisme dev'  # start dev servers
prisme devcontainer logs             # view logs
prisme devcontainer down --volumes   # full teardown
```

## Running Tests Locally

Backend tests can run against SQLite in-memory (the default) since PostgreSQL-specific types like `ARRAY` are automatically swapped to compatible alternatives. For full fidelity, you can also use the devcontainer's PostgreSQL database:

```bash
DATABASE_URL="postgresql+asyncpg://postgres:postgres@<container-ip>:5432/<db>" \
  uv run pytest tests/ -x --tb=short
```

Inside the devcontainer, the `DATABASE_URL` environment variable is already configured, so you can run tests directly:

```bash
prisme devcontainer exec 'cd packages/backend && uv run pytest tests/ -x'
```

## Factory Boy + Async SQLAlchemy

When using Factory Boy with async SQLAlchemy (common in generated test suites), each `SubFactory` creates its own session by default. This causes `INSERT` operations to happen on different connections, breaking foreign key relationships.

Use an autouse fixture to propagate the test session to all factories:

```python
@pytest.fixture(autouse=True)
async def _set_factory_sessions(db_session):
    """Propagate the test DB session to all Factory Boy factories."""
    for factory_class in [ProjectFactory, SiteFactory, ...]:
        factory_class._meta.sqlalchemy_session = db_session
    yield
```

This ensures all factories share the same transaction, so parent objects created by `SubFactory` are visible when inserting child rows.

## Signup Whitelist in Tests

When using `whitelist` signup mode, the signup endpoint rejects emails not in the whitelist. Tests that exercise signup must add whitelist rules first:

```python
async def test_signup(client, db_session):
    # Add email to whitelist before signup
    whitelist = SignupWhitelist(email="test@example.com")
    db_session.add(whitelist)
    await db_session.commit()

    response = await client.post("/api/auth/signup", json={...})
    assert response.status_code == 200
```

## Troubleshooting

### Container won't start

Check Docker is running:
```bash
docker info
```

### Can't access services

Ensure the Prism proxy is running:
```bash
docker ps | grep prism-proxy
```

Start it if needed:
```bash
prisme dev --docker
```

### Permission issues

Use `--root` for system-level operations:
```bash
prisme devcontainer shell myproject --root
apt-get update && apt-get install something
```

### Reset container completely

```bash
prisme devcontainer destroy myproject --volumes
prisme devcontainer start https://github.com/user/repo.git --name myproject
```
